cd micropeak && $(MAKE) fat-install
endif
-fat:
- cd src && $(MAKE) all
- cd doc && $(MAKE) all
+fat: all-recursive
cd libaltos && $(MAKE) fat
cd altoslib && $(MAKE) all
- cd altosuilib && $(MAKE) all
cd icon && $(MAKE) fat
cd altosui && $(MAKE) fat
cd micropeak && $(MAKE) fat
src/easymini-v3.0/easymini-v3.0-$(VERSION).ihx \
src/easymotor-v3/easymotor-v3-$(VERSION).ihx \
src/easytimer-v1/easytimer-v1-$(VERSION).ihx \
+ src/easytimer-v2/easytimer-v2-$(VERSION).ihx \
src/telebt-v3.0/telebt-v3.0-$(VERSION).ihx \
src/telebt-v4.0/telebt-v4.0-$(VERSION).ihx \
src/teledongle-v3.0/teledongle-v3.0-$(VERSION).ihx \
sudo cowbuilder --update
- make sure fat build environment is up to date
- sudo apt update && sudo apt upgrade
+ sudo apt update && sudo apt upgrade && sudo apt autoremove
- ensure i386 build support is available, and we have tools to build
installers for Windows and Mac OS X
sudo apt update
sudo apt install genisoimage nsis \
gcc-i686-linux-gnu gcc-aarch64-linux-gnu \
- gcc-arm-linux-gnueabi gcc-arm-linux-gnueabihf
+ gcc-arm-linux-gnueabi gcc-arm-linux-gnueabihf \
+ gcc-mingw-w64-i686-posix gcc-mingw-w64-x86-64-win32
- - make sure jsign is installed so we can sign Windows installers
+ - make sure jsign is installed so we can sign Windows installers:
+
+ https://github.com/ebourg/jsign/releases/download/5.0/jsign_5.0_all.deb
- make sure ~/web/altusmetrum has no pending pullable commits
src/easymini-v[1-3].0/{*.elf,*.ihx,*.map} \
src/easymotor-v3/{*.elf,*.ihx,*.map} \
src/easytimer-v1/{*.elf,*.ihx,*.map} \
+ src/easytimer-v2/{*.elf,*.ihx,*.map} \
src/telebt-v[3-4].0/{*.elf,*.ihx,*.map} \
src/teledongle-v3.0/{*.elf,*.ihx,*.map} \
src/telegps-v[1-3].0/{*.elf,*.ihx,*.map} \
src/easymini-v[1-3].0/flash-loader/*.elf \
src/easymotor-v3/flash-loader/*.elf \
src/easytimer-v1/flash-loader/*.elf \
+ src/easytimer-v2/flash-loader/*.elf \
src/telebt-v[3-4].0/flash-loader/{*.elf,*.bin,*.map} \
src/teledongle-v3.0/flash-loader/*.elf \
src/telegps-v[1-3].0/flash-loader/{*.elf,*.bin,*.map} \
public int report_feet;
+ /* HAS_GPS_MOSAIC */
+ public int gps_receiver;
+
/* Storage info replies */
public int storage_size;
public int storage_erase_unit;
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_EASYMEGA_3:
case AltosLib.AO_LOG_FORMAT_EASYMOTOR:
/* ADXL375 */
return -value;
+ case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
+ /* BMI088 */
+ return -value;
default:
if (product.startsWith("EasyTimer-"))
return -value;
report_feet = AltosLib.MISSING;
+ gps_receiver = AltosLib.MISSING;
+
tracker_motion = AltosLib.MISSING;
tracker_interval = AltosLib.MISSING;
try { report_feet = get_int(line, "Report in feet:"); } catch (Exception e) {}
+ try { gps_receiver = get_int(line, "GPS receiver:"); } catch (Exception e) {}
+
/* HAS_TRACKER */
try {
int[] values = get_values(line, "Tracker setting:");
if (report_feet != AltosLib.MISSING)
report_feet = source.report_feet();
+ if (gps_receiver != AltosLib.MISSING)
+ gps_receiver = source.gps_receiver();
+
/* HAS_TRACKER */
if (tracker_motion != AltosLib.MISSING)
tracker_motion = source.tracker_motion();
dest.set_beep(beep);
dest.set_radio_10mw(radio_10mw);
dest.set_report_feet(report_feet);
+ dest.set_gps_receiver(gps_receiver);
dest.set_tracker_motion(tracker_motion);
dest.set_tracker_interval(tracker_interval);
}
if (radio_10mw != AltosLib.MISSING)
link.printf("c p %d\n", radio_10mw);
- /* HAS_RADIO_10MW */
if (report_feet != AltosLib.MISSING)
link.printf("c u %d\n", report_feet);
+ /* HAS_GPS_MOSAIC */
+ if (gps_receiver != AltosLib.MISSING)
+ link.printf("c g %d\n", gps_receiver);
+
/* HAS_TRACKER */
if (tracker_motion != AltosLib.MISSING && tracker_interval != AltosLib.MISSING)
link.printf("c t %d %d\n", tracker_motion, tracker_interval);
public abstract void set_radio_10mw(int radio_10mw);
+ public abstract boolean has_radio();
+
public abstract int report_feet() throws AltosConfigDataException;
- public abstract void set_report_feet(int radio_10mw);
+ public abstract void set_report_feet(int report_feet);
+
+ public abstract int gps_receiver() throws AltosConfigDataException;
+
+ public abstract void set_gps_receiver(int gps_receiver);
}
public static int beep_freq_to_value(double freq) {
if (freq == 0)
- return 94;
+ return 0;
return (int) Math.floor (1.0/2.0 * (24.0e6/32.0) / freq + 0.5);
}
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_3:
record = new AltosEepromRecordMega(eeprom);
break;
case AltosLib.AO_LOG_FORMAT_TELEMETRUM:
case AltosLib.AO_LOG_FORMAT_EASYMOTOR:
record = new AltosEepromRecordMotor(eeprom);
break;
+ case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
+ record = new AltosEepromRecordTimer(eeprom);
+ break;
}
ordered = new TreeSet<AltosEepromRecord>();
--- /dev/null
+/*
+ * Copyright © 2024 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.
+ */
+
+package org.altusmetrum.altoslib_14;
+
+public class AltosEepromRecordTimer extends AltosEepromRecord {
+ public static final int record_length = 32;
+
+ private int log_format;
+
+ private int imu_type() {
+ switch (log_format) {
+ case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
+ return AltosIMU.imu_type_easytimer_v2;
+ default:
+ return AltosLib.MISSING;
+ }
+ }
+
+ private int imu_model() {
+ switch (log_format) {
+ case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
+ return AltosLib.model_bmi088;
+ }
+ return AltosLib.MISSING;
+ }
+
+ private boolean sensor_normalized() {
+ switch (log_format) {
+ case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
+ return true;
+ }
+ return false;
+ }
+
+ private int mag_model() {
+ switch (log_format) {
+ case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
+ return AltosLib.model_mmc5983;
+ }
+ return AltosLib.MISSING;
+ }
+
+ /* AO_LOG_FLIGHT elements */
+ private int flight() { return data16(0); }
+ private int ground_accel() { return data16(2); }
+ private int ground_pres() { return AltosLib.MISSING; }
+ private int ground_accel_along() { return data16(4); }
+ private int ground_accel_across() { return data16(6); }
+ private int ground_accel_through() { return data16(8); }
+ private int ground_roll() { return data32(12); }
+ private int ground_pitch() { return data32(16); }
+ private int ground_yaw() { return data32(20); }
+
+ /* AO_LOG_STATE elements */
+ private int state() { return data16(0); }
+ private int reason() { return data16(2); }
+
+ /* AO_LOG_SENSOR elements */
+
+ private int accel_along() { return data16(0); }
+ private int accel_across() { return data16(2); }
+ private int accel_through() { return data16(4); }
+ private int gyro_roll() { return data16(6); }
+ private int gyro_pitch() { return data16(8); }
+ private int gyro_yaw() { return data16(10); }
+ private int mag_along() { return data16(12); }
+ private int mag_across() { return data16(14); }
+ private int mag_through() { return data16(16); }
+
+ private int accel() { return -accel_along(); }
+
+ private int v_batt() { return data16(18); }
+ private int v_pbatt() { return data16(20); }
+ private int nsense() { return 2; }
+ private int sense(int i) { return data16(22 + i * 2); }
+ private int pyro() { return data16(26); }
+
+ public void provide_data(AltosDataListener listener, AltosCalData cal_data) {
+ super.provide_data(listener, cal_data);
+
+ cal_data.set_imu_type(imu_type());
+ cal_data.set_imu_model(imu_model());
+ cal_data.set_mag_model(mag_model());
+
+ switch (cmd()) {
+ case AltosLib.AO_LOG_FLIGHT:
+ cal_data.set_flight(flight());
+ cal_data.set_ground_accel(ground_accel());
+ listener.set_accel_ground(cal_data.accel_along(ground_accel_along()),
+ cal_data.accel_across(ground_accel_across()),
+ cal_data.accel_through(ground_accel_through()));
+ cal_data.set_gyro_zero(ground_roll() / 512.0,
+ ground_pitch() / 512.0,
+ ground_yaw() / 512.0);
+ break;
+ case AltosLib.AO_LOG_STATE:
+ listener.set_state(state());
+ break;
+ case AltosLib.AO_LOG_SENSOR:
+ AltosConfigData config_data = eeprom.config_data();
+
+ int accel_along = accel_along();
+ int accel_across = accel_across();
+ int accel_through = accel_through();
+ int gyro_roll = gyro_roll();
+ int gyro_pitch = gyro_pitch();
+ int gyro_yaw = gyro_yaw();
+
+ int mag_along = mag_along();
+ int mag_across = mag_across();
+ int mag_through = mag_through();
+
+ listener.set_accel(cal_data.accel_along(accel_along),
+ cal_data.accel_across(accel_across),
+ cal_data.accel_through(accel_through));
+ listener.set_gyro(cal_data.gyro_roll(gyro_roll),
+ cal_data.gyro_pitch(gyro_pitch),
+ cal_data.gyro_yaw(gyro_yaw));
+
+ listener.set_mag(cal_data.mag_along(mag_along),
+ cal_data.mag_across(mag_across),
+ cal_data.mag_through(mag_through));
+
+ listener.set_acceleration(cal_data.acceleration(accel()));
+
+ listener.set_battery_voltage(AltosConvert.mega_battery_voltage(v_batt()));
+ listener.set_pyro_voltage(AltosConvert.mega_pyro_voltage(v_pbatt()));
+
+ int nsense = nsense();
+
+ listener.set_apogee_voltage(AltosConvert.mega_pyro_voltage(sense(nsense-2)));
+ listener.set_main_voltage(AltosConvert.mega_pyro_voltage(sense(nsense-1)));
+
+ double voltages[] = new double[nsense-2];
+ for (int i = 0; i < nsense-2; i++)
+ voltages[i] = AltosConvert.mega_pyro_voltage(sense(i));
+
+ listener.set_igniter_voltage(voltages);
+ listener.set_pyro_fired(pyro());
+ break;
+ }
+ }
+
+ public AltosEepromRecord next() {
+ int s = next_start();
+ if (s < 0)
+ return null;
+ return new AltosEepromRecordTimer(eeprom, s);
+ }
+
+ public AltosEepromRecordTimer(AltosEeprom eeprom, int start) {
+ super(eeprom, start, record_length);
+ log_format = eeprom.config_data().log_format;
+ }
+
+ public AltosEepromRecordTimer(AltosEeprom eeprom) {
+ this(eeprom, 0);
+ }
+}
case imu_type_easymega_v1:
case imu_type_easymega_v2:
return counts_per_g_mpu;
- case imu_type_telemega_v4:
+ case imu_type_telemega_v4:
case imu_type_easytimer_v1:
return counts_per_g_bmx;
case imu_type_easymotor_v2:
return counts_per_g_adxl;
+ case imu_type_easytimer_v2:
+ return counts_per_g_bmi088;
}
return AltosLib.MISSING;
public static final int imu_type_easymotor_v2 = 6; /* ADXL375 (accel only) */
+ public static final int imu_type_easytimer_v2 = 7; /* BMI088 */
+
private int accel_across(int imu_type) {
if (accel_across != AltosLib.MISSING)
}
private int accel_along(int imu_type) {
- if (accel_along != AltosLib.MISSING)
+ if (accel_along != AltosLib.MISSING) {
+ System.out.printf("accel along %d\n", accel_along);
return accel_along;
+ }
switch (imu_type) {
case imu_type_telemega_v1_v2:
private static boolean is_primary_accel(int imu_type) {
switch (imu_type) {
case imu_type_easytimer_v1:
+ case imu_type_easytimer_v2:
return true;
default:
return false;
AltosIMU imu = new AltosIMU(link);
AltosCalData cal_data = listener.cal_data();
+ System.out.printf("imu_model %d mag_model %d\n", imu.imu_model, imu.mag_model);
if (imu_type != AltosLib.MISSING)
cal_data.set_imu_type(imu_type);
if (imu != null) {
static final int idle_adxl375 = 10;
static final int idle_adxl375_easymotor_v2 = 11;
static final int idle_imu = 12;
+ static final int idle_imu_et_v2 = 13;
+ static final int idle_imu_em_v3 = 14;
static final int idle_sensor_tm = 100;
static final int idle_sensor_metrum = 101;
static final int idle_sensor_easytimer1 = 110;
static final int idle_sensor_easymotor2 = 111;
static final int idle_sensor_emini3 = 112;
+ static final int idle_sensor_etimer2 = 113;
+ static final int idle_sensor_emega3 = 114;
public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException, TimeoutException, AltosUnknownProduct {
for (int idler : idlers) {
case idle_imu_et_v1:
AltosIMU.provide_data(listener, link, AltosIMU.imu_type_easytimer_v1);
break;
+ case idle_imu_et_v2:
+ AltosIMU.provide_data(listener, link, AltosIMU.imu_type_easytimer_v2);
+ break;
case idle_imu:
AltosIMU.provide_data(listener, link, AltosLib.MISSING);
break;
AltosIdler.idle_ms5607,
AltosIdler.idle_imu_em_v2,
AltosIdler.idle_sensor_mega),
+ new AltosIdler("EasyMega-v3",
+ AltosIdler.idle_adxl375,
+ AltosIdler.idle_ms5607,
+ AltosIdler.idle_imu,
+ AltosIdler.idle_mag,
+ AltosIdler.idle_sensor_mega),
new AltosIdler("TeleGPS-v1",
AltosIdler.idle_gps,
AltosIdler.idle_sensor_tgps1),
new AltosIdler("EasyMotor-v2",
AltosIdler.idle_adxl375_easymotor_v2,
AltosIdler.idle_sensor_easymotor2),
+ new AltosIdler("EasyTimer-v2",
+ AltosIdler.idle_imu_et_v2,
+ AltosIdler.idle_sensor_easymotor2),
};
AltosLink link;
public final static int product_basestation = 0x10000 + 1;
public final static int product_altimeter = 0x10000 + 2;
+ public final static int gps_builtin = 0;
+ public final static int gps_mosaic = 1;
+
+ public final static String[] gps_receiver_names = {
+ "Builtin", "Mosaic-X5"
+ };
+
private static class Product {
final String name;
final int product;
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_EASYTIMER_2 = 23;
+ public static final int AO_LOG_FORMAT_EASYMEGA_3 = 24;
public static final int AO_LOG_FORMAT_NONE = 127;
public static final int model_mpu6000 = 0;
case product_telegps: return "TeleGPS";
case product_easymini: return "EasyMini";
case product_telemini: return "TeleMini";
+ case product_easymega: return "EasyMega";
case product_easymotor: return "EasyMotor";
default: return "unknown";
}
case AO_LOG_FORMAT_TELEMEGA_3:
return product_telemega;
case AO_LOG_FORMAT_EASYMEGA_2:
+ case AO_LOG_FORMAT_EASYMEGA_3:
return product_easymega;
case AO_LOG_FORMAT_TELESTATIC:
return product_altusmetrum;
case AltosIMU.imu_type_telemega_v1_v2:
case AltosIMU.imu_type_easymega_v1:
return counts_per_gauss_hmc5883;
+ case AltosIMU.imu_type_easytimer_v2:
+ return counts_per_gauss_mmc5983;
}
return AltosIMU.counts_per_gauss(imu_type, mag_model);
--- /dev/null
+/*
+ * Copyright © 2020 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.
+ */
+
+package org.altusmetrum.altoslib_14;
+
+import java.util.concurrent.TimeoutException;
+
+class AltosSensorEasyTimer2 {
+ int tick;
+ int[] sense;
+ int v_batt;
+ int v_pbatt;
+ int temp;
+
+ public AltosSensorEasyTimer2() {
+ sense = new int[2];
+ }
+
+ public AltosSensorEasyTimer2(AltosLink link) throws InterruptedException, TimeoutException {
+ this();
+ String[] items = link.adc();
+ for (int i = 0; i < items.length;) {
+ if (items[i].equals("tick:")) {
+ tick = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("A:")) {
+ sense[0] = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("B:")) {
+ sense[1] = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("batt:")) {
+ v_batt = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ i++;
+ }
+ }
+
+ static public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException {
+ try {
+ AltosSensorEasyTimer2 sensor_easytimer2 = new AltosSensorEasyTimer2(link);
+
+ listener.set_battery_voltage(AltosConvert.easy_timer_voltage(sensor_easytimer2.v_batt));
+
+ double[] igniter_voltage = new double[2];
+ for (int i = 0; i < 2; i++)
+ igniter_voltage[i] = AltosConvert.easy_timer_voltage(sensor_easytimer2.sense[i]);
+ listener.set_igniter_voltage(igniter_voltage);
+
+ } catch (TimeoutException te) {
+ }
+ }
+}
+
AltosEepromRecordMicroPeak2.java \
AltosEepromRecordMotor.java \
AltosEepromRecordSet.java \
+ AltosEepromRecordTimer.java \
AltosEepromChunk.java \
AltosEepromDownload.java \
AltosEepromMonitor.java \
AltosSensorMM.java \
AltosSensorEMini.java \
AltosSensorEasyTimer1.java \
+ AltosSensorEasyTimer2.java \
AltosSensorTM.java \
AltosSensorTMini2.java \
AltosSensorTMini3.java \
JLabel radio_enable_label;
JLabel radio_10mw_label;
JLabel report_feet_label;
+ JLabel gps_receiver_label;
JLabel rate_label;
JLabel aprs_interval_label;
JLabel aprs_ssid_label;
JRadioButton radio_enable_value;
JRadioButton radio_10mw_value;
JComboBox<String> report_feet_value;
+ JComboBox<String> gps_receiver_value;
AltosUIRateList rate_value;
JComboBox<String> aprs_interval_value;
JComboBox<Integer> aprs_ssid_value;
return product != null && product.startsWith("EasyTimer");
}
- boolean has_radio() {
+ public boolean has_radio() {
return is_telemega() || is_telemetrum() || is_telemini();
}
void set_beep_tool_tip() {
if (beep_value.isVisible())
- beep_value.setToolTipText("What frequency the beeper will sound at");
+ beep_value.setToolTipText("What frequency the beeper will sound at (0 for off)");
else
beep_value.setToolTipText("Older firmware could not select beeper frequency");
}
report_feet_value.setToolTipText("Older firmware always beeps max height in meters");
}
+ void set_gps_receiver_tool_tip() {
+ if (gps_receiver_value.isVisible())
+ gps_receiver_value.setToolTipText("GPS receiver selection");
+ else
+ gps_receiver_value.setToolTipText("Only TeleMega with new firmware supports alternate GPS receivers");
+ }
+
/* Build the UI using a grid bag */
public AltosConfigFCUI(JFrame in_owner, boolean remote) {
super (in_owner, title, false);
set_report_feet_tool_tip();
row++;
+ /* GPS Receiver */
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = row;
+ c.gridwidth = 4;
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ c.ipady = 5;
+ gps_receiver_label = new JLabel("GPS Receiver:");
+ pane.add(gps_receiver_label, c);
+
+ c = new GridBagConstraints();
+ c.gridx = 4; c.gridy = row;
+ c.gridwidth = 4;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ c.ipady = 5;
+ gps_receiver_value = new JComboBox<String>(AltosLib.gps_receiver_names);
+ gps_receiver_value.setEditable(false);
+ gps_receiver_value.addItemListener(this);
+ pane.add(gps_receiver_value, c);
+ set_gps_receiver_tool_tip();
+ row++;
+
/* Telemetry Rate */
c = new GridBagConstraints();
c.gridx = 0; c.gridy = row;
return AltosLib.MISSING;
}
+ public void set_gps_receiver(int new_gps_receiver) {
+ System.out.printf("set_gps_receiver %d\n", new_gps_receiver);
+ if (new_gps_receiver != AltosLib.MISSING) {
+ if (new_gps_receiver >= AltosLib.gps_receiver_names.length)
+ new_gps_receiver = 0;
+ if (new_gps_receiver < 0) {
+ gps_receiver_value.setEnabled(false);
+ new_gps_receiver = 0;
+ } else {
+ gps_receiver_value.setEnabled(true);
+ }
+ gps_receiver_value.setSelectedIndex(new_gps_receiver);
+ }
+ gps_receiver_value.setVisible(new_gps_receiver != AltosLib.MISSING);
+ gps_receiver_label.setVisible(new_gps_receiver != AltosLib.MISSING);
+
+ set_gps_receiver_tool_tip();
+ }
+
+ public int gps_receiver() {
+ if (gps_receiver_value.isVisible())
+ return gps_receiver_value.getSelectedIndex();
+ else
+ return AltosLib.MISSING;
+ }
+
String[] tracker_motion_values() {
if (AltosConvert.imperial_units)
return tracker_motion_values_ft;
FIRMWARE_EMOTOR=$(FIRMWARE_EMOTOR_3)
FIRMWARE_ETIMER_1=$(top_srcdir)/src/easytimer-v1/easytimer-v1-$(VERSION).ihx
-FIRMWARE_ETIMER=$(FIRMWARE_ETIMER_1)
+FIRMWARE_ETIMER_2=$(top_srcdir)/src/easytimer-v2/easytimer-v2-$(VERSION).ihx
+FIRMWARE_ETIMER=$(FIRMWARE_ETIMER_1) $(FIRMWARE_ETIMER_2)
FIRMWARE_TGPS_1_0=$(top_srcdir)/src/telegps-v1.0/telegps-v1.0-$(VERSION).ihx
FIRMWARE_TGPS_2_0=$(top_srcdir)/src/telegps-v2.0/telegps-v2.0-$(VERSION).ihx
File "../src/easymega-v2.0/easymega-v2.0-${VERSION}.ihx"
File "../src/easymotor-v3/easymotor-v3-${VERSION}.ihx"
File "../src/easytimer-v1/easytimer-v1-${VERSION}.ihx"
+ File "../src/easytimer-v2/easytimer-v2-${VERSION}.ihx"
File "../src/telelco-v2.0/telelco-v2.0-${VERSION}.ihx"
File "../src/telefireeight-v1.0/telefireeight-v1.0-${VERSION}.ihx"
File "../src/telefireeight-v2.0/telefireeight-v2.0-${VERSION}.ihx"
public void run() {
switch (phase) {
case AltosAccelCal.phase_antenna_up:
- message.setText("Orient antenna upwards and click on Antenna Up");
+ message.setText(String.format ("Orient antenna upwards and click on %s", up_msg()));
antenna_up.setEnabled(true);
setDefaultButton(antenna_up);
antenna_down.setEnabled(false);
ok.setEnabled(false);
break;
case AltosAccelCal.phase_antenna_down:
- message.setText("Orient antenna downwards and click on Antenna Down");
+ message.setText(String.format("Orient antenna downwards and click on %s", down_msg()));
antenna_up.setEnabled(false);
antenna_down.setEnabled(true);
setDefaultButton(antenna_down);
}
}
}
+
+ public String up_msg() { return config_values.has_radio() ? "Antenna Up" : "Beeper Up"; }
+ public String down_msg() { return config_values.has_radio() ? "Antenna Down" : "Beeper Down"; }
+
public AltosUIAccelCal(Frame owner, AltosLink link, AltosConfigValues config_values) {
super(owner, "Calibrate Accelerometer", true);
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
- antenna_up = new JButton("Antenna Up");
+ antenna_up = new JButton(up_msg());
antenna_up.setActionCommand("up");
antenna_up.setEnabled(false);
antenna_up.addActionListener(this);
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
- antenna_down = new JButton("Antenna Down");
+ antenna_down = new JButton(down_msg());
antenna_down.setActionCommand("down");
antenna_down.setEnabled(false);
antenna_down.addActionListener(this);
-bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x ao-reset-lpc ao-flash-samd21
+bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x ao-reset-lpc ao-flash-samd21 ao-flash-stm32f1
-man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1 ao-reset-lpc.1 ao-flash-samd21.1
+man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1 ao-reset-lpc.1 ao-flash-samd21.1 ao-flash-stm32f1.1
--- /dev/null
+#!/bin/sh
+case "$#" in
+0)
+ echo "usage: $0 <filename> ..."
+ exit 1
+ ;;
+esac
+openocd \
+ -f interface/stlink.cfg \
+ -c 'transport select hla_swd' \
+ -f target/stm32f1x.cfg \
+ -c init \
+ -c 'reset halt' \
+ -c "program $1 verify reset" \
+ -c 'shutdown'
--- /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.
+.\"
+.\"
+.TH AO-FLASH-LPC 1 "ao-flash-stm32f1" ""
+.SH NAME
+ao-flash-stm32f1 \- flash a program to an STM32F1x-based AltOS device using openocd
+.SH SYNOPSIS
+.B "ao-flash-stm32f1"
+\fIfile.elf\fP
+.SH DESCRIPTION
+.I ao-flash-stm32f1
+loads the specified .elf file into the target device flash memory.
+.SH USAGE
+.I ao-flash-stm32f1
+is a simple script that passes the correct arguments to openocd to
+load a file into the target device via a connected STlink
+debugging dongle.
+.SH "SEE ALSO"
+openocd(1)
+.SH AUTHOR
+Keith Packard
dnl Process this file with autoconf to create configure.
AC_PREREQ(2.57)
-AC_INIT([altos], 1.9.17)
+AC_INIT([altos], 1.9.18)
ANDROID_VERSION=37
AC_CONFIG_SRCDIR([src/kernel/ao.h])
AM_INIT_AUTOMAKE([foreign dist-bzip2])
AM_MAINTAINER_MODE
-RELEASE_DATE=2023-08-30
+RELEASE_DATE=2024-04-28
AC_SUBST(RELEASE_DATE)
DOC_DATE=`LC_ALL=C date -d $RELEASE_DATE +'%d %b %Y'`
case x"$MULTI_ARCH" in
xauto)
- arch=`uname -m`
- case x"$arch" in
- xx86_64|xi*86)
- save_CFLAGS="$CFLAGS"
- save_LIBS="$LIBS"
- LIBS="-ldl"
- CFLAGS="-m64"
- AC_MSG_CHECKING([if ]$CC[ ]$CFLAGS[ can link programs])
- AC_LINK_IFELSE([AC_LANG_PROGRAM([])],
- [M64_LINK=yes],
- [M64_LINK=no])
- AC_MSG_RESULT([$M64_LINK])
- CFLAGS="-m32"
- AC_MSG_CHECKING([if ]$CC[ ]$CFLAGS[ can link programs])
- AC_LINK_IFELSE([AC_LANG_PROGRAM([])],
- [M32_LINK=yes],
- [M32_LINK=no])
- AC_MSG_RESULT([$M32_LINK])
- CFLAGS="$save_CFLAGS"
- LIBS="$save_LIBS"
- case x"$M64_LINK"x"$M32_LINK" in
- xyesxyes)
- MULTI_ARCH=yes
+ MULTI_ARCH=yes
+ for arch in i686-linux-gnu x86_64-linux-gnu aarch64-linux-gnu arm-linux-gnueabi arm-linux-gnueabihf; do
+ crossgcc="$arch"-gcc
+ AC_CHECK_PROG(ARCH_SUPPORTED,$crossgcc,yes,no)
+ case "$ARCH_SUPPORTED" in
+ yes)
;;
*)
MULTI_ARCH=no
;;
esac
- ;;
- *)
- MULTI_ARCH=no
- ;;
- esac
+ done
+ AC_MSG_CHECKING([MULTI_ARCH])
+ AC_MSG_RESULT([$MULTI_ARCH])
;;
xyes|xno)
;;
echo " Android support.............: ${HAVE_ANDROID_SDK}"
echo " Android release support.....: ${ANDROID_RELEASE}"
echo " STlink support..............: ${HAVE_STLINK}"
-echo " i386 and amd64 libaltos.....: ${MULTI_ARCH}"
+echo " multi-arch libaltos.........: ${MULTI_ARCH}"
echo " install shared mime info....: ${INSTALL_SHARED_MIME_INFO}"
echo " Strip jar timestamps........: ${STRIP_NONDETERMINISM}"
echo ""
endif
RELNOTES_INC=\
+ release-notes-1.9.18.inc \
release-notes-1.9.17.inc \
release-notes-1.9.16.inc \
release-notes-1.9.15.inc \
asciidoctor $(ATTRIBUTES) -b html5 $*.adoc
.adoc.pdf:
- asciidoctor-pdf $(ATTRIBUTES) $*.adoc
+ asciidoctor-pdf $(ATTRIBUTES) -a optimize $*.adoc
all: $(HTML) $(PDF)
left:
content: '{page-number}'
right:
- content: '© 2023 Bdale Garbee and Keith Packard. Creative Commons ShareAlike 3.0 License'
+ content: '© 2024 Bdale Garbee and Keith Packard. Creative Commons ShareAlike 3.0 License'
verso:
left:
content: $footer_recto_right_content
:revdate: 1 Jan 1970
:icons:
:icontype: svg
-:copyright: Bdale Garbee and Keith Packard 2023
+:copyright: Bdale Garbee and Keith Packard 2024
:doctype: book
:numbered:
:stylesheet: am.css
[appendix]
== Release Notes
+ :leveloffset: 2
+ include::release-notes-1.9.18.adoc[]
+
+ <<<<
+ :leveloffset: 2
+ include::release-notes-1.9.17.adoc[]
+
+ <<<<
:leveloffset: 2
include::release-notes-1.9.16.adoc[]
[license]
== License
-Copyright © 2023 Bdale Garbee and Keith Packard
+Copyright © 2024 Bdale Garbee and Keith Packard
This document is released under the terms of the link:http://creativecommons.org/licenses/by-sa/3.0/[Creative Commons ShareAlike 3.0 License]
connector, are readily available through various vendors
including Amazon, eBay, and AliExpress.
+ These pressure sensors have three wires and sometimes a
+ shield around those wires. The colors of the wires can vary,
+ but typically are red for power, black for ground, and green
+ for the analog output. Hook red to '+5', green to 'PRES', and
+ black to one of the two 'GND' screws on the board. The other
+ 'GND' screw is available for connecting the shield if one is
+ present. This is mostly relevant if you're using the product
+ for static testing on the ground with a longer than usual
+ cable.
+
To log in-flight chamber pressure, a typical approach
might be to drill a 1/8" sampling hole all the way
through the center of the motor's forward closure, then
If you aren't up for machining closures yourself,
link:http://lokiresearch.com[Loki Research] sells
54mm and 75mm "experimental bulkheads" with threaded
- sensor ports that can be used with snap ring cases.
+ sensor ports that can be used with snap ring cases,
+ and other related mechanical bits and pieces.
the previous stage has completed providing acceleration, to
minimize drag of the sustainer's coast phase before ignition.
Recovery, whether the remainder of the flight is nominal or
- not, usually works best when the states are separated. So,
+ not, usually works best when the stages are separated. So,
the "best" way to configure a pyro channel for a separation
charge is to just set "after motor number". For a 2-stage
project, set this to "1". This will cause the pyro channel
--- /dev/null
+= Release Notes for Version 1.9.18
+include::release-head.adoc[]
+:doctype: article
+
+ Version 1.9.18
+
+ == AltOS
+
+ * Add support for EasyTimer V2. The new version of this
+ product has on-board storage to log data during flight.
+
+ == AltosUI & TeleGPS application
+
+ * Add support for EasyTimer V2. This includes support for
+ analyizing flight data from the on-board logs.
+
+ * Allow on-board beepers to be disabled by setting the
+ frequency to 0.
[appendix]
== Release Notes
+ :leveloffset: 2
+ include::release-notes-1.9.18.adoc[]
+
+ <<<<
:leveloffset: 2
include::release-notes-1.9.17.adoc[]
|-
|-
|3.7-12V
+
+ |EasyTimer v2.0
+ |-
+ |24g
+ |-
+ |BMI088
+ |1MB
+ |-
+ |3.7-12V
endif::easytimer[]
ifdef::easymotor[]
[appendix]
== Release Notes
+ :leveloffset: 2
+ include::release-notes-1.9.18.adoc[]
+
+ <<<<
+ :leveloffset: 2
+ include::release-notes-1.9.17.adoc[]
+
+ <<<<
:leveloffset: 2
include::release-notes-1.9.16.adoc[]
LINUX_MIMETYPE =\
$(ICONDIR)/org-altusmetrum-mimetypes.xml
+FIRMWARE_MP_0_1=$(top_srcdir)/src/micropeak/micropeak-v0.1-$(VERSION).ihx
+FIRMWARE_MP=$(FIRMWARE_MP_0_1)
+FIRMWARE_MS_1_0=$(top_srcdir)/src/microsplash/microsplash-v1.0-$(VERSION).ihx
+FIRMWARE_MS=$(FIRMWARE_MS_1_0)
+
+FIRMWARE=$(FIRMWARE_MP) $(FIRMWARE_MS)
+
desktopdir = $(datadir)/applications
desktop_file = altusmetrum-micropeak.desktop
desktop_SCRIPTS = $(desktop_file)
MACOSX_README=ReadMe-Mac.rtf
MACOSX_INSTALL=../altosui/install-macosx ../altosui/ask-pass
MACOSX_FILES=$(FAT_FILES) libaltos.dylib $(MACOSX_INFO_PLIST) $(MACOSX_DRIVERS) $(MACOSX_README) $(DOC) $(MACOSX_ICONS) $(MACOSX_INSTALL)
+MACOSX_EXTRA=$(FIRMWARE)
$(MACOSX_DRIVER_0):
wget -O $@ $(MACOSX_DRIVER_0_URL)
$(WINDOWS_DRIVER):
wget -O "$(WINDOWS_DRIVER)" "$(WINDOWS_DRIVER_URL)"
-WINDOWS_FILES=$(FAT_FILES) altos.dll altos64.dll $(DOC) $(WINDOWS_ICONS) $(WINDOWS_DRIVER)
+WINDOWS_FILES=$(FAT_FILES) $(FIRMWARE) altos.dll altos64.dll $(DOC) $(WINDOWS_ICONS) $(WINDOWS_DRIVER)
if FATINSTALL
sed 's/AltOS/MicroPeak/g' $(srcdir)/../altosui/linux-install.sh | cat - $(LINUX_DIST) > $@
chmod +x $@
-$(MACOSX_DIST): $(MACOSX_FILES)
+$(MACOSX_DIST): $(MACOSX_FILES) $(MACOSX_EXTRA) Makefile
-rm -f $@
-rm -rf macosx
mkdir macosx
cp -a $(DOC) macosx/Doc
cp -p Info.plist macosx/MicroPeak.app/Contents
cp -p $(MACOSX_DRIVERS) macosx
- mkdir -p macosx/MicroPeak.app/Contents/Resources/Java
+ mkdir -p macosx/MicroPeak-$(VERSION) macosx/MicroPeak.app/Contents/Resources/Java
cp -p $(MACOSX_ICONS) macosx/MicroPeak.app/Contents/Resources
cp -p $(FATJAR) macosx/MicroPeak.app/Contents/Resources/Java/micropeak.jar
cp -p libaltos.dylib macosx/MicroPeak.app/Contents/Resources/Java
cp -p $(ALTOSUILIB_CLASS) macosx/MicroPeak.app/Contents/Resources/Java
cp -p $(JFREECHART_CLASS) macosx/MicroPeak.app/Contents/Resources/Java
cp -p $(JCOMMON_CLASS) macosx/MicroPeak.app/Contents/Resources/Java
+ cp -p $(MACOSX_EXTRA) macosx/MicroPeak-$(VERSION)
genisoimage -D -V MicroPeak-$(VERSION) -no-pad -r -apple -o $@ macosx
$(WINDOWS_DIST): $(WINDOWS_FILES) micropeak-windows.nsi
ExecWait $2
SectionEnd
+Section "Firmware"
+
+ SetOutPath $INSTDIR
+ File "../src/micropeak/micropeak-v0.1-${VERSION}.ihx"
+ File "../src/microsplash/microsplash-v1.0-${VERSION}.ihx"
+
+SectionEnd
+
Section "Documentation"
SetOutPath $INSTDIR
easymega-v2.0 easymega-v2.0/flash-loader \
easymotor-v3 easymotor-v3/flash-loader \
easytimer-v1 easytimer-v1/flash-loader \
+ easytimer-v2 easytimer-v2/flash-loader \
telemega-v0.1 telemega-v0.1/flash-loader \
telemega-v1.0 telemega-v1.0/flash-loader \
telemega-v2.0 telemega-v2.0/flash-loader \
telemini-v3.0 telemini-v3.0/flash-loader \
easymini-v2.0 easymini-v2.0/flash-loader \
easymini-v3.0 easymini-v3.0/flash-loader \
+ telelco-v3.0 telelco-v3.0/flash-loader \
+ easymega-v3.0 easymega-v3.0/flash-loader \
micropeak-v2.0
AVRDIRS=\
micropeak microkite microsplash
-SUBDIRS=
+SUBDIRS=draw
ifeq ($(strip $(HAVE_ARM_M3_CC)),yes)
SUBDIRS+=$(ARMM3DIRS)
.bdf.c:
nickle font-convert -o $@ $<
-all: ao_font.h ao_logo.h lco-test line-test
+all: ao_font.h ao_logo.h
+
+test: lco-test line-test
$(FONT_SRCS): font-convert
TEST_LIBS=-lXrender -lXext -lX11 -lm -Wl,--gc-sections
-CFLAGS=-O0 -g $(WARN_FLAGS) -DVALIDATE -I.
+CFLAGS=-O3 -g $(WARN_FLAGS) -DVALIDATE -I.
HEADERS=\
ao_draw.h \
struct ao_box damage;
};
+#define AO_BITMAP_STRIDE(width) (((width) + 31) >> 5)
+
struct ao_coord {
float x, y;
};
int8_t advance;
};
+struct ao_text_metrics {
+ int16_t width;
+ int16_t height;
+ int16_t x_off;
+ int16_t y_off;
+ int16_t advance;
+};
+
struct ao_font {
const uint8_t *bytes;
const uint16_t *pos;
uint32_t fill,
uint8_t rop);
-void
+int16_t
ao_text(struct ao_bitmap *dst,
const struct ao_font *font,
int16_t x,
int16_t y,
- char *string,
+ const char *string,
uint32_t fill,
uint8_t rop);
+int16_t
+ao_text_width(const struct ao_font *font,
+ const char *string);
+
+void
+ao_logo_poly(struct ao_bitmap *dst,
+ const struct ao_transform *transform,
+ uint32_t fill,
+ uint8_t rop);
+
void
ao_logo(struct ao_bitmap *dst,
const struct ao_transform *transform,
#define AO_NAND 0xe /* NOT src OR NOT dst */
#define AO_SET 0xf /* 1 */
+#define AO_WHITE 0xffffffff
+#define AO_BLACK 0x00000000
+
#endif /* _AO_DRAW_H_ */
* isn't symmetrical when the line passes exactly between
* two pixels, we have to pick which one gets drawn
*/
- int32_t adj_min;
-
if (c->e3) {
+ int32_t adj_min;
+
if (!c->first)
adj_min = div_ceil(c->e + adjust_major * c->e1, -c->e3);
else
adj_min = div_floor_plus_one(c->e + adjust_major * c->e1, -c->e3);
- }
- if (adj_min < adjust_minor) {
- if (c->e1) {
- if (c->first)
- adjust_major = div_ceil(c->e - adjust_minor * c->e3, c->e1);
- else
- adjust_major = div_floor_plus_one(c->e - adjust_minor * c->e3, c->e1);
+ if (adj_min < adjust_minor) {
+ if (c->e1) {
+ if (c->first)
+ adjust_major = div_ceil(c->e - adjust_minor * c->e3, c->e1);
+ else
+ adjust_major = div_floor_plus_one(c->e - adjust_minor * c->e3, c->e1);
+ }
+ } else {
+ adjust_minor = adj_min;
}
- } else {
- adjust_minor = adj_min;
}
-
c->e = (int16_t) (c->e + adjust_major * c->e1 + adjust_minor * c->e3);
c->major = (int16_t) (c->major + c->sign_major * adjust_major);
#define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0]))
+void
+ao_logo_poly(struct ao_bitmap *dst,
+ const struct ao_transform *transform,
+ uint32_t fill,
+ uint8_t rop)
+{
+ if (!transform)
+ transform = &ao_identity;
+ ao_poly(dst, ao_logo_top, ARRAYSIZE(ao_logo_top), transform, fill, rop);
+ ao_poly(dst, ao_logo_bottom, ARRAYSIZE(ao_logo_bottom), transform, fill, rop);
+}
+
void
ao_logo(struct ao_bitmap *dst,
const struct ao_transform *transform,
int16_t name_x = ao_t_xi(ao_logo_width, 0.0f, transform);
int16_t name_y1 = ao_t_yi(ao_logo_width, 0.5f, transform);
int16_t name_y2 = ao_t_yi(ao_logo_width, 0.98f, transform);
- ao_poly(dst, ao_logo_top, ARRAYSIZE(ao_logo_top), transform, fill, rop);
- ao_poly(dst, ao_logo_bottom, ARRAYSIZE(ao_logo_bottom), transform, fill, rop);
+ ao_logo_poly(dst, transform, fill, rop);
ao_text(dst, font, name_x, name_y1, "Altus", fill, rop);
ao_text(dst, font, name_x, name_y2, "Metrum", fill, rop);
}
#include <string.h>
#include <stdio.h>
-void
+extern uint32_t ao_glyph_temp[];
+
+static struct ao_bitmap src_bitmap = {
+ .base = ao_glyph_temp,
+};
+
+int16_t
ao_text(struct ao_bitmap *dst,
const struct ao_font *font,
int16_t x,
int16_t y,
- char *string,
+ const char *string,
uint32_t fill,
uint8_t rop)
{
int16_t glyph_stride = ao_stride(font->max_width);
- uint32_t src[glyph_stride * font->max_height];
char c;
int h;
int16_t x_off = 0, y_off = 0, advance = 0;
int16_t byte_width = 0;
- struct ao_bitmap src_bitmap = {
- .base = src,
- };
-
rop = (rop & 3) | 0x4;
if ((fill&1) == 0)
}
for (h = 0; h < src_bitmap.height; h++)
- memcpy(&src[h * src_bitmap.stride], &bytes[h * byte_width], (size_t) byte_width);
+ memcpy(&ao_glyph_temp[h * src_bitmap.stride], &bytes[h * byte_width], (size_t) byte_width);
ao_copy(dst,
x + x_off, y - y_off, src_bitmap.width, src_bitmap.height,
0, 0, rop);
x += advance;
}
+ return x;
}
--- /dev/null
+/*
+ * Copyright © 2024 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_draw.h>
+#include <ao_draw_int.h>
+#include "ao_font.h"
+#include <string.h>
+#include <stdio.h>
+
+int16_t
+ao_text_width(const struct ao_font *font,
+ const char *string)
+{
+ char c;
+ int16_t x = 0;
+
+ while ((c = *string++)) {
+ if (font->metrics) {
+ x += font->metrics[(uint8_t) c].advance;
+ } else {
+ x += font->max_width;
+ }
+ }
+ return x;
+}
fprintf(out, "\t.max_height = %d,\n", max_height);
fprintf(out, "\t.ascent = %d,\n", font.ascent);
fprintf(out, "};\n");
+ int max_stride = (max_width + 31) >> 5;
+ fprintf(out, "__asm__(\".balign 4\\n.comm ao_glyph_temp 0x%x\");\n", max_stride * max_height * 4);
}
string
#define PRINTD(l,...)
#endif
-#define AO_ADXL375_SPI_SPEED ao_spi_speed(5000000)
+#define AO_ADXL375_SPI_SPEED ao_spi_speed(AO_ADXL375_SPI_INDEX, 5000000)
struct ao_adxl375_sample ao_adxl375_current;
#include <ao_bmi088.h>
#include <ao_data.h>
-#define AO_BMI088_SPI_SPEED ao_spi_speed(100000)
+#define AO_BMI088_SPI_SPEED ao_spi_speed(AO_BMI088_SPI_BUS, 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)
static struct ao_bmm150_trim ao_bmm150_trim;
-#define AO_BMX160_SPI_SPEED ao_spi_speed(10000000)
+#define AO_BMX160_SPI_SPEED ao_spi_speed(AO_BMX160_SPI_BUS, 10000000)
#define ao_bmx160_spi_get() ao_spi_get(AO_BMX160_SPI_BUS, AO_BMX160_SPI_SPEED)
#define ao_bmx160_spi_put() ao_spi_put(AO_BMX160_SPI_BUS)
#define FOSC 32000000
-#define AO_CC1120_SPI_SPEED ao_spi_speed(6100000) /* 6.1MHz max with 32MHz osc */
+#define AO_CC1120_SPI_SPEED ao_spi_speed(AO_CC1120_SPI_BUS, 6100000) /* 6.1MHz max with 32MHz osc */
#define ao_radio_try_select(task_id) ao_spi_try_get_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS,AO_CC1120_SPI_SPEED, task_id)
#define ao_radio_select() ao_spi_get_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS,AO_CC1120_SPI_SPEED)
#define FOSC 26000000
-#define AO_CC115L_SPI_SPEED ao_spi_speed(6500000) /* for back-to-back access */
+#define AO_CC115L_SPI_SPEED ao_spi_speed(AO_CC115L_SPI_BUS, 6500000) /* for back-to-back access */
#define ao_radio_select() ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_CC115L_SPI_SPEED)
#define ao_radio_deselect() ao_spi_put_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS)
#define FOSC 40000000
#endif
-#define AO_CC1200_SPI_SPEED ao_spi_speed(7700000) /* 7.7MHz max for extended memory reads */
+#define AO_CC1200_SPI_SPEED ao_spi_speed(AO_CC1200_SPI_BUS, 7700000) /* 7.7MHz max for extended memory reads */
#define ao_radio_select() ao_spi_get_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS,AO_CC1200_SPI_SPEED)
#define ao_radio_deselect() ao_spi_put_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS)
#error HAS_COMPANION not set in ao_companion.c
#endif
-#define AO_COMPANION_SPI_SPEED ao_spi_speed(200000)
+#define AO_COMPANION_SPI_SPEED ao_spi_speed(AO_COMPANION_SPI_BUS, 200000)
#define COMPANION_SELECT() do { \
ao_spi_get_bit(AO_COMPANION_CS_PORT, \
#define AO_LCO_DRAG_RACE_START_TIME AO_SEC_TO_TICKS(5)
#define AO_LCO_DRAG_RACE_STOP_TIME AO_SEC_TO_TICKS(2)
-#define AO_LCO_BOX_DRAG 0x1000
-
/* UI values */
static AO_TICK_TYPE ao_lco_fire_tick;
static uint8_t ao_lco_fire_down;
static uint8_t ao_lco_display_mutex;
void
-ao_lco_show_pad(uint8_t pad)
+ao_lco_show_pad(int8_t pad)
{
ao_mutex_get(&ao_lco_display_mutex);
ao_seven_segment_set(AO_LCO_PAD_DIGIT, (uint8_t) (pad | (ao_lco_drag_race << 4)));
(0 << 6))
void
-ao_lco_show_box(uint16_t box)
+ao_lco_show_box(int16_t box)
{
ao_mutex_get(&ao_lco_display_mutex);
if (box == AO_LCO_BOX_DRAG) {
ao_lco_show_voltage(ao_pad_query.battery);
} else {
if (ao_lco_box == AO_LCO_BOX_DRAG)
- ao_lco_show_pad(ao_lco_drag_race);
+ ao_lco_show_pad((int8_t) ao_lco_drag_race);
else
ao_lco_show_pad(ao_lco_pad);
ao_lco_show_box(ao_lco_box);
}
}
-uint8_t
-ao_lco_box_present(uint16_t box)
-{
- if (box == AO_LCO_BOX_DRAG)
- return 1;
-
- if (box >= AO_PAD_MAX_BOXES)
- return 0;
- return (ao_lco_box_mask[AO_LCO_MASK_ID(box)] >> AO_LCO_MASK_SHIFT(box)) & 1;
-}
-
static struct ao_task ao_lco_drag_task;
static uint8_t ao_lco_drag_active;
}
}
-static void
-ao_lco_step_box(int8_t dir)
-{
- int32_t new_box = (int32_t) ao_lco_box;
- do {
- if (new_box == AO_LCO_BOX_DRAG) {
- if (dir < 0)
- new_box = ao_lco_max_box;
- else
- new_box = ao_lco_min_box;
- } else {
- new_box += dir;
- if (new_box > ao_lco_max_box)
- new_box = AO_LCO_BOX_DRAG;
- else if (new_box < ao_lco_min_box)
- new_box = AO_LCO_BOX_DRAG;
- }
- if (new_box == (int32_t) ao_lco_box)
- break;
- } while (!ao_lco_box_present((uint16_t) new_box));
- ao_lco_set_box((uint16_t) new_box);
-}
-
static void
ao_lco_input(void)
{
extern uint8_t ao_lco_drag_race; /* true when drag race mode enabled */
#endif
-extern uint8_t ao_lco_pad; /* Currently selected pad */
-extern uint16_t ao_lco_box; /* Currently selected box */
+extern int8_t ao_lco_pad; /* Currently selected pad */
+extern int16_t ao_lco_box; /* Currently selected box */
extern uint8_t ao_lco_armed; /* armed mode active */
extern uint8_t ao_lco_firing; /* fire button pressed */
extern struct ao_pad_query ao_pad_query; /* Last received QUERY from pad */
+#ifdef AO_LCO_DRAG_RACE_BOX
+#define AO_LCO_BOX_DRAG 0 /* Box number to enable drag race mode (old LCO bits) */
+#define AO_LCO_BOX_FIRST AO_LCO_BOX_DRAG
+#else
+# define AO_LCO_LCO_VOLTAGE 0 /* Box number to show LCO voltage */
+# ifdef AO_LCO_HAS_INFO
+# define AO_LCO_INFO -3
+# ifndef AO_LCO_BOX_FIRST
+# define AO_LCO_BOX_FIRST AO_LCO_INFO
+# endif
+# endif
+# ifdef AO_LCO_HAS_BACKLIGHT
+# define AO_LCO_BACKLIGHT -2
+# ifndef AO_LCO_BOX_FIRST
+# define AO_LCO_BOX_FIRST AO_LCO_BACKLIGHT
+# endif
+# endif
+# ifdef AO_LCO_HAS_CONTRAST
+# define AO_LCO_CONTRAST -1
+# ifndef AO_LCO_BOX_FIRST
+# define AO_LCO_BOX_FIRST AO_LCO_CONTRAST
+# endif
+# endif
+# ifndef AO_LCO_BOX_FIRST
+# define AO_LCO_BOX_FIRST AO_LCO_LCO_VOLTAGE
+# endif
+#endif
#define AO_LCO_PAD_VOLTAGE 0 /* Pad number to show box voltage */
-
-extern uint16_t ao_lco_min_box, ao_lco_max_box;
+#define AO_LCO_PAD_RSSI -1 /* Pad number to show box RSSI */
+#define AO_LCO_PAD_FIRST AO_LCO_PAD_RSSI
+
+static inline bool
+ao_lco_box_pseudo(int16_t box)
+{
+ switch (box) {
+#ifdef AO_LCO_LCO_VOLTAGE
+ case AO_LCO_LCO_VOLTAGE:
+ return true;
+#endif
+#ifdef AO_LCO_DRAG_RACE_BOX
+ case AO_LCO_BOX_DRAG:
+ return true;
+#endif
+#ifdef AO_LCO_CONTRAST
+ case AO_LCO_CONTRAST:
+ return true;
+#endif
+#ifdef AO_LCO_BACKLIGHT
+ case AO_LCO_BACKLIGHT:
+ return true;
+#endif
+#ifdef AO_LCO_INFO
+ case AO_LCO_INFO:
+ return true;
+#endif
+ default:
+ return false;
+ }
+}
+
+static inline bool
+ao_lco_pad_pseudo(int8_t pad)
+{
+ switch (pad) {
+ case AO_LCO_PAD_VOLTAGE:
+ return true;
+ case AO_LCO_PAD_RSSI:
+ return true;
+ default:
+ return false;
+ }
+}
+
+extern int16_t ao_lco_min_box, ao_lco_max_box;
#define AO_LCO_MASK_SIZE(n) (((n) + 7) >> 3)
#define AO_LCO_MASK_ID(n) ((n) >> 3)
extern uint8_t ao_lco_box_mask[AO_LCO_MASK_SIZE(AO_PAD_MAX_BOXES)];
+#define AO_LCO_VALID_LAST 1
+#define AO_LCO_VALID_EVER 2
+
+extern uint8_t ao_lco_valid[AO_PAD_MAX_BOXES]; /* AO_LCO_VALID bits per box */
+
/*
* Shared functions
*/
ao_lco_update(void);
uint8_t
-ao_lco_pad_present(uint16_t box, uint8_t pad);
+ao_lco_pad_present(int16_t box, int8_t pad);
-uint8_t
-ao_lco_pad_first(uint16_t box);
+int8_t
+ao_lco_pad_first(int16_t box);
void
-ao_lco_set_pad(uint8_t new_pad);
+ao_lco_set_pad(int8_t new_pad);
void
ao_lco_step_pad(int8_t dir);
void
-ao_lco_set_box(uint16_t new_box);
+ao_lco_set_box(int16_t new_box);
+
+void
+ao_lco_step_box(int8_t dir);
void
ao_lco_set_armed(uint8_t armed);
void
ao_lco_monitor(void);
-extern uint8_t ao_lco_drag_beep_count;
+extern int8_t ao_lco_drag_beep_count;
/* enable drag race mode */
void
/* Request 'beeps' additional drag race beeps */
void
-ao_lco_drag_add_beeps(uint8_t beeps);
+ao_lco_drag_add_beeps(int8_t beeps);
/* task function for beeping while arm is active */
void
*/
void
-ao_lco_show_pad(uint8_t pad);
+ao_lco_show_pad(int8_t pad);
void
-ao_lco_show_box(uint16_t box);
+ao_lco_show_box(int16_t box);
void
ao_lco_show(void);
ao_lco_init(void);
uint8_t
-ao_lco_box_present(uint16_t box);
+ao_lco_box_present(int16_t box);
+
+#ifdef AO_LCO_HAS_CONTRAST
+void
+ao_lco_set_contrast(int32_t contrast);
+
+int32_t
+ao_lco_get_contrast(void);
+#endif
+
+#ifdef AO_LCO_HAS_BACKLIGHT
+void
+ao_lco_set_backlight(int32_t backlight);
+
+int32_t
+ao_lco_get_backlight(void);
+#endif
+
+#ifdef AO_LCO_SEARCH_API
+
+void
+ao_lco_search_start(void);
+
+void
+ao_lco_search_box_check(int16_t box);
+
+void
+ao_lco_search_box_present(int16_t box);
+
+void
+ao_lco_search_done(void);
+
+#endif /* AO_LCO_SEARCH_API */
#endif /* _AO_LCO_H_ */
uint8_t ao_lco_debug;
-uint8_t ao_lco_pad;
-uint16_t ao_lco_box;
+int8_t ao_lco_pad;
+int16_t ao_lco_box;
uint8_t ao_lco_armed; /* arm active */
uint8_t ao_lco_firing; /* fire active */
-uint16_t ao_lco_min_box, ao_lco_max_box;
+int16_t ao_lco_min_box, ao_lco_max_box;
uint8_t ao_lco_pretending;
static uint16_t ao_lco_tick_offset[AO_PAD_MAX_BOXES]; /* 16 bit offset from local to remote tick count */
static uint8_t ao_lco_selected[AO_PAD_MAX_BOXES]; /* pads selected to fire */
-#define AO_LCO_VALID_LAST 1
-#define AO_LCO_VALID_EVER 2
-
-static uint8_t ao_lco_valid[AO_PAD_MAX_BOXES]; /* AO_LCO_VALID bits per box */
+uint8_t ao_lco_valid[AO_PAD_MAX_BOXES]; /* AO_LCO_VALID bits per box */
static const AO_LED_TYPE continuity_led[AO_LED_CONTINUITY_NUM] = {
#ifdef AO_LED_CONTINUITY_0
else
#endif
ao_sleep(&ao_pad_query);
+ if (ao_lco_box_pseudo(ao_lco_box)) {
+ ao_led_off(AO_LED_GREEN|AO_LED_AMBER|AO_LED_RED);
+ continue;
+ }
PRINTD("RSSI %d VALID %d\n", ao_radio_cmac_rssi, ao_lco_valid[ao_lco_box]);
if (!(ao_lco_valid[ao_lco_box] & AO_LCO_VALID_LAST)) {
ao_led_on(AO_LED_RED);
}
uint8_t
-ao_lco_pad_present(uint16_t box, uint8_t pad)
+ao_lco_pad_present(int16_t box, int8_t pad)
{
/* voltage measurement is always valid */
- if (pad == AO_LCO_PAD_VOLTAGE)
+ if (ao_lco_pad_pseudo(pad))
return 1;
if (!ao_lco_channels[box])
return 0;
return (ao_lco_channels[box] >> (pad - 1)) & 1;
}
-uint8_t
-ao_lco_pad_first(uint16_t box)
+int8_t
+ao_lco_pad_first(int16_t box)
{
- uint8_t pad;
+ int8_t pad;
for (pad = 1; pad <= AO_PAD_MAX_CHANNELS; pad++)
if (ao_lco_pad_present(box, pad))
}
static uint8_t
-ao_lco_get_channels(uint16_t box, struct ao_pad_query *query)
+ao_lco_get_channels(int16_t box, struct ao_pad_query *query)
{
int8_t r;
- r = ao_lco_query(box, query, &ao_lco_tick_offset[box]);
+ r = ao_lco_query((uint16_t) box, query, &ao_lco_tick_offset[box]);
if (r == AO_RADIO_CMAC_OK) {
ao_lco_channels[box] = query->channels;
ao_lco_valid[box] = AO_LCO_VALID_LAST | AO_LCO_VALID_EVER;
void
ao_lco_update(void)
{
+ if (ao_lco_box_pseudo(ao_lco_box)) {
+ ao_lco_show();
+ return;
+ }
+
uint8_t previous_valid = ao_lco_valid[ao_lco_box];
if (ao_lco_get_channels(ao_lco_box, &ao_pad_query) & AO_LCO_VALID_LAST) {
if (!(previous_valid & AO_LCO_VALID_EVER)) {
- if (ao_lco_pad != AO_LCO_PAD_VOLTAGE)
+ if (!ao_lco_pad_pseudo(ao_lco_pad))
ao_lco_set_pad(ao_lco_pad_first(ao_lco_box));
}
- if (ao_lco_pad == AO_LCO_PAD_VOLTAGE)
+ if (ao_lco_pad_pseudo(ao_lco_pad))
ao_lco_show();
}
}
}
static void
-ao_lco_box_set_present(uint16_t box)
+ao_lco_box_set_present(int16_t box)
{
if (box < ao_lco_min_box)
ao_lco_min_box = box;
}
void
-ao_lco_set_pad(uint8_t new_pad)
+ao_lco_set_pad(int8_t new_pad)
{
ao_lco_pad = new_pad;
ao_lco_show();
}
void
-ao_lco_set_box(uint16_t new_box)
+ao_lco_set_box(int16_t new_box)
{
ao_lco_box = new_box;
- 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;
+ if (!ao_lco_box_pseudo(ao_lco_box)) {
+ 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();
{
int16_t new_pad;
- new_pad = (int16_t) ao_lco_pad;
+ switch (ao_lco_box) {
+#ifdef AO_LCO_HAS_CONTRAST
+ case AO_LCO_CONTRAST: {
+ int32_t contrast = ao_lco_get_contrast();
+
+ contrast = (contrast + AO_LCO_CONTRAST_STEP - 1) / AO_LCO_CONTRAST_STEP;
+ contrast += dir;
+ contrast *= AO_LCO_CONTRAST_STEP;
+ if (contrast < AO_LCO_MIN_CONTRAST)
+ contrast = AO_LCO_MIN_CONTRAST;
+ if (contrast > AO_LCO_MAX_CONTRAST)
+ contrast = AO_LCO_MAX_CONTRAST;
+ ao_lco_set_contrast(contrast);
+ ao_lco_show();
+ break;
+ }
+#endif
+#ifdef AO_LCO_HAS_BACKLIGHT
+ case AO_LCO_BACKLIGHT: {
+ int32_t backlight = ao_lco_get_backlight();
+
+ backlight = (backlight + AO_LCO_BACKLIGHT_STEP - 1) / AO_LCO_BACKLIGHT_STEP;
+ backlight += dir;
+ backlight *= AO_LCO_BACKLIGHT_STEP;
+ if (backlight < AO_LCO_MIN_BACKLIGHT)
+ backlight = AO_LCO_MIN_BACKLIGHT;
+ if (backlight > AO_LCO_MAX_BACKLIGHT)
+ backlight = AO_LCO_MAX_BACKLIGHT;
+ ao_lco_set_backlight(backlight);
+ ao_lco_show();
+ break;
+ }
+#endif
+#ifdef AO_LCO_HAS_INFO
+ case AO_LCO_INFO: {
+#if AO_LCO_MIN_INFO_PAGE < AO_LCO_MAX_INFO_PAGE
+ int32_t info_page = ao_lco_get_info_page();
+
+ info += dir;
+ if (info_page < AO_LCO_MIN_INFO_PAGE)
+ info_page = AO_LCO_MIN_INFO_PAGE;
+ if (info_page > AO_LCO_MAX_INFO_PAGE)
+ info_page = AO_LCO_MAX_INFO_PAGE;
+ ao_lco_set_info_page();
+#endif
+ break;
+ }
+#endif
+ default:
+ new_pad = (int16_t) ao_lco_pad;
+ do {
+ new_pad += dir;
+ if (new_pad > AO_PAD_MAX_CHANNELS)
+ new_pad = AO_LCO_PAD_FIRST;
+ if (new_pad < AO_LCO_PAD_FIRST)
+ new_pad = AO_PAD_MAX_CHANNELS;
+ if (new_pad == ao_lco_pad)
+ break;
+ } while (!ao_lco_pad_present(ao_lco_box, (int8_t) new_pad));
+ PRINTD("New pad %d\n", new_pad);
+ ao_lco_set_pad((int8_t) new_pad);
+ break;
+ }
+}
+
+uint8_t
+ao_lco_box_present(int16_t box)
+{
+ if (ao_lco_box_pseudo(box))
+ return 1;
+ if (box >= AO_PAD_MAX_BOXES)
+ return 0;
+ return (ao_lco_box_mask[AO_LCO_MASK_ID(box)] >> AO_LCO_MASK_SHIFT(box)) & 1;
+}
+
+void
+ao_lco_step_box(int8_t dir)
+{
+ int16_t new_box = ao_lco_box;
+
do {
- new_pad += dir;
- if (new_pad > AO_PAD_MAX_CHANNELS)
- new_pad = AO_LCO_PAD_VOLTAGE;
- if (new_pad < 0)
- new_pad = AO_PAD_MAX_CHANNELS;
- if (new_pad == ao_lco_pad)
+ new_box += dir;
+ if (new_box > ao_lco_max_box)
+ new_box = AO_LCO_BOX_FIRST;
+ else if (new_box < AO_LCO_BOX_FIRST)
+ new_box = ao_lco_max_box;
+ if (new_box == ao_lco_box)
break;
- } while (!ao_lco_pad_present(ao_lco_box, (uint8_t) new_pad));
- ao_lco_set_pad((uint8_t) new_pad);
+ } while (!ao_lco_box_present(new_box));
+ PRINTD("New box %d\n", new_box);
+ ao_lco_set_box(new_box);
}
void
ao_lco_set_armed(uint8_t armed)
{
+ if (ao_lco_box_pseudo(ao_lco_box))
+ return;
+
ao_lco_armed = armed;
PRINTD("Armed %d\n", ao_lco_armed);
if (ao_lco_armed) {
#if AO_LCO_DRAG
if (ao_lco_drag_race) {
- uint16_t box;
+ int16_t box;
for (box = ao_lco_min_box; box <= ao_lco_max_box; box++)
if (ao_lco_selected[box])
ao_wakeup(&ao_lco_armed);
}
+#if 0
+static int16_t fake_boxes[] = {
+ 1, 2, 3, 5, 8, 11, 13, 17, 19, 23, 29, 31, 37, 62, 97
+};
+#define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0]))
+#define NFAKE ARRAYSIZE(fake_boxes)
+
+static bool
+is_fake(int16_t box)
+{
+ unsigned i;
+ for (i = 0; i < NFAKE; i++)
+ if (fake_boxes[i] == box)
+ return true;
+ return false;
+}
+#else
+#define is_fake(b) false
+#endif
+
void
ao_lco_search(void)
{
int8_t r;
int8_t try;
- uint16_t box;
+ int16_t box;
uint16_t boxes = 0;
ao_lco_box_reset_present();
+#ifdef AO_LCO_SEARCH_API
+ ao_lco_search_start();
+#else
ao_lco_show_box(0);
ao_lco_show_pad(0);
+#endif
for (box = 0; box < AO_PAD_MAX_BOXES; box++) {
+#ifdef AO_LCO_SEARCH_API
+ ao_lco_search_box_check(box);
+#else
if ((box % 10) == 0)
ao_lco_show_box(box);
+#endif
for (try = 0; try < 3; try++) {
ao_lco_tick_offset[box] = 0;
- r = ao_lco_query(box, &ao_pad_query, &ao_lco_tick_offset[box]);
+ r = ao_lco_query((uint16_t) box, &ao_pad_query, &ao_lco_tick_offset[box]);
PRINTD("box %d result %d offset %d\n", box, r, ao_lco_tick_offset[box]);
- if (r == AO_RADIO_CMAC_OK) {
+ if (r == AO_RADIO_CMAC_OK || is_fake(box)) {
++boxes;
ao_lco_box_set_present(box);
- ao_lco_show_pad((uint8_t) (boxes % 10));
+#ifdef AO_LCO_SEARCH_API
+ ao_lco_search_box_present(box);
+#else
+ ao_lco_show_pad((int8_t) (boxes % 10));
+#endif
ao_delay(AO_MS_TO_TICKS(30));
break;
}
ao_lco_min_box = ao_lco_max_box = ao_lco_box = 0;
memset(ao_lco_valid, 0, sizeof (ao_lco_valid));
memset(ao_lco_channels, 0, sizeof (ao_lco_channels));
+#ifdef AO_LCO_SEARCH_API
+ ao_lco_search_done();
+#endif
ao_lco_set_box(ao_lco_min_box);
}
void
ao_lco_pretend(void)
{
- uint16_t box;
+ int16_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++)
+ 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));
void
ao_lco_monitor(void)
{
- AO_TICK_TYPE delay;
- uint16_t box;
+ AO_TICK_TYPE delay;
+ int16_t box;
for (;;) {
PRINTD("monitor armed %d firing %d\n",
PRINTD("Arming box %d pads %x\n",
box, ao_lco_selected[box]);
if (ao_lco_valid[box] & AO_LCO_VALID_EVER) {
- ao_lco_arm(box, ao_lco_selected[box], ao_lco_tick_offset[box]);
+ ao_lco_arm((uint16_t) box, ao_lco_selected[box], ao_lco_tick_offset[box]);
ao_delay(AO_MS_TO_TICKS(10));
}
}
#if AO_LCO_DRAG
-uint8_t ao_lco_drag_beep_count;
+int8_t ao_lco_drag_beep_count;
static uint8_t ao_lco_drag_beep_on;
static AO_TICK_TYPE ao_lco_drag_beep_time;
static AO_TICK_TYPE ao_lco_drag_warn_time;
/* Request 'beeps' additional drag race beeps */
void
-ao_lco_drag_add_beeps(uint8_t beeps)
+ao_lco_drag_add_beeps(int8_t beeps)
{
PRINTD("beep %d\n", beeps);
if (ao_lco_drag_beep_count == 0)
void
ao_lco_toggle_drag(void)
{
- if (ao_lco_drag_race && ao_lco_pad != AO_LCO_PAD_VOLTAGE) {
+ if (ao_lco_drag_race && !ao_lco_pad_pseudo(ao_lco_pad) && !ao_lco_box_pseudo(ao_lco_box)) {
ao_lco_selected[ao_lco_box] ^= (uint8_t) (1 << (ao_lco_pad - 1));
PRINTD("Toggle box %d pad %d (pads now %x) to drag race\n",
ao_lco_pad, ao_lco_box, ao_lco_selected[ao_lco_box]);
}
void
-ao_lco_show_pad(uint8_t pad)
+ao_lco_show_pad(int8_t pad)
{
(void) pad;
}
void
-ao_lco_show_box(uint16_t box)
+ao_lco_show_box(int16_t box)
{
(void) box;
}
case AO_EVENT_BUTTON:
switch (event.unit) {
case AO_BUTTON_BOX:
- ao_lco_set_box((uint16_t) event.value);
+ ao_lco_set_box((int16_t) event.value);
ao_lco_set_armed(0);
break;
case AO_BUTTON_ARM:
static uint8_t ao_m25_instruction[4];
-#define AO_M25_SPI_SPEED ao_spi_speed(10000000) /* this seems like a reasonable minimum speed to require */
+#define AO_M25_SPI_SPEED ao_spi_speed(AO_M25_SPI_BUS, 10000000) /* this seems like a reasonable minimum speed to require */
#define M25_SELECT(cs) ao_spi_get_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS,AO_M25_SPI_SPEED)
#define M25_DESELECT(cs) ao_spi_put_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS)
#define PRINTD(l,...)
#endif
-#define AO_MMA655X_SPI_SPEED ao_spi_speed(8333333) /* 120ns clock period */
+#define AO_MMA655X_SPI_SPEED ao_spi_speed(AO_MMA655X_SPI_INDEX, 8333333) /* 120ns clock period */
static void
ao_mma655x_start(void) {
static uint8_t ao_mmc5983_configured;
#ifdef MMC5983_I2C
+#ifdef AO_MMC5983_I2C_INDEX
+
+static void
+ao_mmc5983_start(void) {
+ ao_i2c_get(AO_MMC5983_I2C_INDEX);
+}
+
+static void
+ao_mmc5983_stop(void) {
+ ao_i2c_put(AO_MMC5983_I2C_INDEX);
+}
+
+static void
+ao_mmc5983_reg_write(uint8_t addr, uint8_t data)
+{
+ uint8_t d[2];
+
+ d[0] = addr;
+ d[1] = data;
+
+ ao_mmc5983_start();
+ ao_i2c_start(AO_MMC5983_I2C_INDEX, MMC5983_I2C_ADDR);
+ ao_i2c_send(d, 2, AO_MMC5983_I2C_INDEX, true);
+ ao_mmc5983_stop();
+}
+
+static uint8_t
+ao_mmc5983_reg_read(uint8_t addr)
+{
+ uint8_t d[1];
+
+ ao_mmc5983_start();
+ ao_i2c_start(AO_MMC5983_I2C_INDEX, MMC5983_I2C_ADDR);
+ d[0] = addr;
+ ao_i2c_send(d, 1, AO_MMC5983_I2C_INDEX, false);
+ ao_i2c_start(AO_MMC5983_I2C_INDEX, MMC5983_I2C_ADDR | 1);
+ ao_i2c_recv(d, 1, AO_MMC5983_I2C_INDEX, true);
+ ao_mmc5983_stop();
+ return d[0];
+}
+
+static void
+ao_mmc5983_raw(struct ao_mmc5983_raw *raw)
+{
+ ao_mmc5983_start();
+ ao_i2c_start(AO_MMC5983_I2C_INDEX, MMC5983_I2C_ADDR);
+ raw->addr = MMC5983_X_OUT_0;
+ ao_i2c_send(&(raw->addr), 1, AO_MMC5983_I2C_INDEX, false);
+ ao_i2c_start(AO_MMC5983_I2C_INDEX, MMC5983_I2C_ADDR | 1);
+ ao_i2c_recv(&(raw->x0), sizeof(*raw) - 1, AO_MMC5983_I2C_INDEX, true);
+ ao_mmc5983_stop();
+}
+
+#else
#include <ao_i2c_bit.h>
static void
ao_i2c_bit_stop();
}
+#endif
+
#else
-#define AO_MMC5983_SPI_SPEED ao_spi_speed(2000000)
+#define AO_MMC5983_SPI_SPEED ao_spi_speed(AO_MMC5983_SPI_INDEX, 2000000)
static void
ao_mmc5983_start(void) {
{
ao_mmc5983_configured = 0;
-#ifdef MMC5983_I2C
- ao_enable_output(AO_MMC5983_SPI_CS_PORT, AO_MMC5983_SPI_CS_PIN, 1);
-#else
+#ifndef MMC5983_I2C
ao_enable_input(AO_MMC5983_SPI_MISO_PORT,
AO_MMC5983_SPI_MISO_PIN,
AO_EXTI_MODE_PULL_NONE);
#if AO_MPU6000_SPI
-#define AO_MPU6000_SPI_SPEED ao_spi_speed(1000000) /* 1Mhz for all register access */
+#define AO_MPU6000_SPI_SPEED ao_spi_speed(AO_MPU6000_SPI_BUS, 1000000) /* 1Mhz for all register access */
#define ao_mpu6000_spi_get() ao_spi_get(AO_MPU6000_SPI_BUS, AO_MPU6000_SPI_SPEED)
#define ao_mpu6000_spi_put() ao_spi_put(AO_MPU6000_SPI_BUS)
#if AO_MPU9250_SPI
#ifndef AO_MPU9250_SPI_SPEED
-#define AO_MPU9250_SPI_SPEED ao_spi_speed(1000000) /* 1MHz max SCLK */
+#define AO_MPU9250_SPI_SPEED ao_spi_speed(AO_MPU9250_SPI_BUS, 1000000) /* 1MHz max SCLK */
#endif
#define ao_mpu9250_spi_get() ao_spi_get(AO_MPU9250_SPI_BUS, AO_MPU9250_SPI_SPEED)
struct ao_ms5607_prom ao_ms5607_prom;
static uint8_t ms5607_configured;
-#define AO_MS5607_SPI_SPEED ao_spi_speed(20000000)
+#define AO_MS5607_SPI_SPEED ao_spi_speed(AO_MS5607_SPI_INDEX, 20000000)
static void
ao_ms5607_start(void) {
ao_seven_segment_show(void)
{
uint8_t digit, value;
- digit = ao_cmd_decimal();
- value = ao_cmd_decimal();
+ digit = (uint8_t) ao_cmd_decimal();
+ value = (uint8_t) ao_cmd_decimal();
ao_seven_segment_set(digit, value);
}
--- /dev/null
+/*
+ * Copyright © 2023 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_st7565.h>
+
+static void
+ao_st7565_reset(void)
+{
+ ao_gpio_set(AO_ST7565_RESET_PORT, AO_ST7565_RESET_PIN, 0);
+ ao_delay(AO_MS_TO_TICKS(100));
+ ao_gpio_set(AO_ST7565_RESET_PORT, AO_ST7565_RESET_PIN, 1);
+ ao_delay(AO_MS_TO_TICKS(100));
+}
+
+
+static void
+ao_st7565_start(uint8_t a0)
+{
+ ao_gpio_set(AO_ST7565_A0_PORT, AO_ST7565_A0_PIN, a0);
+ ao_spi_get_bit(AO_ST7565_CS_PORT,
+ AO_ST7565_CS_PIN,
+ AO_ST7565_SPI_BUS,
+ AO_ST7565_SPI_SPEED);
+}
+
+static void
+ao_st7565_stop(void)
+{
+ ao_spi_put_bit(AO_ST7565_CS_PORT,
+ AO_ST7565_CS_PIN,
+ AO_ST7565_SPI_BUS);
+ ao_gpio_set(AO_ST7565_A0_PORT, AO_ST7565_A0_PIN, 1);
+}
+
+
+static void
+ao_st7565_instruction(uint8_t cmd)
+{
+ ao_st7565_start(0);
+ ao_spi_send(&cmd, 1, AO_ST7565_SPI_BUS);
+ ao_st7565_stop();
+}
+
+static void
+ao_st7565_instruction_param(uint8_t cmd, uint8_t param)
+{
+ uint8_t b[2] = { cmd, param };
+ ao_st7565_start(0);
+ ao_spi_send(b, 2, AO_ST7565_SPI_BUS);
+ ao_st7565_stop();
+}
+
+static void
+ao_st7565_instructions(const uint8_t *cmd, uint16_t len)
+{
+ ao_st7565_start(0);
+ ao_spi_send(cmd, len, AO_ST7565_SPI_BUS);
+ ao_st7565_stop();
+}
+
+static void
+ao_st7565_data(const void *base, uint16_t len)
+{
+ ao_st7565_start(1);
+ ao_spi_send(base, len, AO_ST7565_SPI_BUS);
+ ao_st7565_stop();
+}
+
+static uint8_t brightness;
+
+void
+ao_st7565_set_brightness(uint8_t val)
+{
+ if (val > 63)
+ val = 63;
+ brightness = val;
+ ao_st7565_instruction_param(ST7565_ELECTRONIC_VOLUME_SET, val);
+}
+
+uint8_t
+ao_st7565_get_brightness(void)
+{
+ return brightness;
+}
+
+static bool setup_done;
+
+static void
+ao_st7565_setup(void)
+{
+ static const uint8_t init[] = {
+ /*
+ * Should be set to one of ST7565_LCD_BIAS_1_9 or
+ * ST7565_LCD_BIAS_1_7
+ */
+ AO_ST7565_BIAS,
+ ST7565_ADC_SELECT_NORMAL,
+ ST7565_COMMON_MODE_NORMAL,
+ ST7565_DISPLAY_START_LINE_SET(0),
+ ST7565_POWER_CONTROL_SET(0x4),
+ };
+
+ if (setup_done)
+ return;
+ setup_done = true;
+ ao_st7565_reset();
+ ao_st7565_instructions(init, sizeof(init));
+ ao_delay(AO_MS_TO_TICKS(50));
+ ao_st7565_instruction(ST7565_POWER_CONTROL_SET(0x6));
+ ao_delay(AO_MS_TO_TICKS(50));
+ ao_st7565_instruction(ST7565_POWER_CONTROL_SET(0x7));
+ ao_delay(AO_MS_TO_TICKS(10));
+ ao_st7565_instruction(ST7565_RESISTOR_RATIO_SET(5));
+ ao_st7565_instruction(ST7565_DISPLAY_ON);
+ ao_st7565_set_brightness(13);
+}
+
+static uint8_t rotbuf[AO_ST7565_WIDTH];
+
+#define WIDTH AO_ST7565_WIDTH
+#define HEIGHT AO_ST7565_HEIGHT
+#define STRIDE AO_BITMAP_STRIDE(WIDTH)
+
+static uint32_t previous_image[STRIDE * HEIGHT];
+
+void
+ao_st7565_update(struct ao_bitmap *bitmap)
+{
+ int16_t col, c, page;
+ int16_t row;
+ uint32_t *line, *prev, *l;
+ uint32_t bits;
+ uint8_t *r;
+ int16_t min_col, min_row, max_col, max_row;
+ int16_t min_page, max_page;
+
+ ao_st7565_setup();
+
+ min_col = STRIDE - 1;
+ max_col = 0;
+ min_row = HEIGHT - 1;
+ max_row = 0;
+ line = bitmap->base;
+ prev = previous_image;
+ for (row = 0; row < HEIGHT; row++) {
+ for (col = 0; col < STRIDE; col++) {
+ bits = *line++;
+ if (bits != *prev) {
+ *prev = bits;
+ if (row < min_row)
+ min_row = row;
+ if (row > max_row)
+ max_row = row;
+ if (col < min_col)
+ min_col = col;
+ if (col > max_col)
+ max_col = col;
+ }
+ prev++;
+ }
+ }
+
+ if (min_col > max_col || min_row > max_row)
+ return;
+
+ min_page = min_row >> 3;
+ max_page = max_row >> 3;
+ line = bitmap->base + min_page * 8 * STRIDE + min_col;
+
+ uint8_t first_col = (uint8_t) (min_col * 32);
+ uint8_t num_col = (uint8_t) (max_col + 1 - min_col) * 32;
+
+ for (page = min_page; page <= max_page; page++) {
+ uint8_t i[4] = {
+ ST7565_PAGE_ADDRESS_SET(7-(uint8_t) page),
+ ST7565_COLUMN_ADDRESS_SET_MSN(first_col >> 4),
+ ST7565_COLUMN_ADDRESS_SET_LSN(first_col & 0xf),
+ ST7565_RMW
+ };
+ memset(rotbuf, 0, num_col);
+ for (row = 7; row >= 0; row--) {
+ r = rotbuf;
+ l = line;
+ line += STRIDE;
+ for (col = min_col; col <= max_col; col++) {
+ bits = ~*l++;
+ for (c = 0; c < 32; c++) {
+ *r++ |= ((bits >> c) & 1) << row;
+ }
+ }
+ }
+ ao_st7565_instructions(i, 4);
+ ao_st7565_data(rotbuf, num_col);
+ }
+}
+
+void
+ao_st7565_init(void)
+{
+ memset(previous_image, 0xff, sizeof(previous_image));
+ ao_enable_output(AO_ST7565_RESET_PORT, AO_ST7565_RESET_PIN, 1);
+ ao_enable_output(AO_ST7565_A0_PORT, AO_ST7565_A0_PIN, 1);
+
+ ao_enable_cs(AO_ST7565_CS_PORT, AO_ST7565_CS_PIN);
+}
--- /dev/null
+/*
+ * Copyright © 2023 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_ST7565_H_
+#define _AO_ST7565_H_
+
+#include <ao_draw.h>
+
+/*
+ * Wiring the NewHaven NHD-C12864LZ-FSW-FBW-3V3 display
+ *
+ * PIN Symbol Connection
+ * 1 /CS1 Chip select. Active low.
+ * 2 /RES Reset. Active low.
+ * 3 A0 Register select. 0: instruction, 1: data
+ * 4 /WR NC
+ * 5 /RD NC
+ * 6 DB0 NC
+ * 7 DB1 NC
+ * 8 DB2 NC
+ * 8 DB3 NC
+ * 10 DB4 NC
+ * 11 DB5 NC
+ * 12 DB6 SPI clock. Mode 3 (idle high, rising edge)
+ * 13 DB7 SPI data.
+ * 14 Vdd +3.3V
+ * 15 Vss Ground
+ * 16 Vout 1uF to ground
+ * 17 CAP3+ 1uF to CAP1-
+ * 18 CAP1- 1uF to CAP3+ and CAP1+
+ * 19 CAP1+ 1uF to CAP1-
+ * 20 CAP2+ 1uF to CAP2-
+ * 21 CAP2- 1uF to CAP2+
+ * 22 V4 1uF to ground
+ * 23 V3 1uF to ground
+ * 24 V2 1uF to ground
+ * 25 V1 1uF to ground
+ * 26 V0 1uF to ground
+ * 27 C86 MPU select. Ground
+ * 28 PS Parallel/serial select. Ground
+ */
+
+#define ST7565_DISPLAY_OFF 0xae
+#define ST7565_DISPLAY_ON 0xaf
+#define ST7565_DISPLAY_START_LINE_SET(line) (0x40 | (line))
+#define ST7565_PAGE_ADDRESS_SET(page) (0xb0 | (page))
+#define ST7565_COLUMN_ADDRESS_SET_MSN(nib) (0x10 | (nib))
+#define ST7565_COLUMN_ADDRESS_SET_LSN(nib) (0x00 | (nib))
+#define ST7565_ADC_SELECT_NORMAL 0xa0
+#define ST7565_ADC_SELECT_REVERSE 0xa1
+#define ST7565_DISPLAY_NORMAL 0xa6
+#define ST7565_DISPLAY_REVERSE 0xa7
+#define ST7565_DISPLAY_ALL_POINTS_OFF 0xa4
+#define ST7565_DISPLAY_ALL_POINTS_ON 0xa5
+#define ST7565_LCD_BIAS_1_9 0xa2
+#define ST7565_LCD_BIAS_1_7 0xa3
+#define ST7565_RMW 0xe0
+#define ST7565_RMW_END 0xee
+#define ST7565_RESET 0xe2
+#define ST7565_COMMON_MODE_NORMAL 0xc0
+#define ST7565_COMMON_MODE_REVERSE 0xc8
+#define ST7565_POWER_CONTROL_SET(pc) (0x28 | (pc))
+#define ST7565_RESISTOR_RATIO_SET(rr) (0x20 | (rr))
+#define ST7565_ELECTRONIC_VOLUME_SET 0x81
+#define ST7565_SLEEP_MODE 0xac
+#define ST7565_BOOSTER_RATIO_SET 0xf8
+#define ST7565_NOP 0xe3
+
+/* Max 50ns SPI clock time */
+#define AO_ST7565_SPI_SPEED ao_spi_speed(AO_ST7565_SPI_BUS, 20000000)
+
+void
+ao_st7565_update(struct ao_bitmap *bitmap);
+
+void
+ao_st7565_set_brightness(uint8_t val);
+
+uint8_t
+ao_st7565_get_brightness(void);
+
+void
+ao_st7565_init(void);
+
+#endif /* _AO_ST7565_H_ */
#
#
-include ../stm/Makefile.defs
+include ../stm32f1/Makefile.defs
INC = \
ao.h \
ao_ms5607.h \
ao_bmx160.h \
ao_adxl375.h \
- ao_profile.h \
ao_task.h \
ao_whiten.h \
ao_sample_profile.h \
ao_quaternion.h \
- ao_mpu.h \
- stm32l.h \
+ stm32f1.h \
Makefile
#
# Common AltOS sources
#
-#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_clock.c \
ao_product.c \
ao_romconfig.c \
ao_cmd.c \
ao_config.c \
ao_task.c \
- ao_led_stm.c \
+ ao_led.c \
ao_stdio.c \
ao_panic.c \
ao_timer.c \
ao_freq.c \
ao_dma_stm.c \
ao_spi_stm.c \
+ ao_i2c_stm.c \
ao_data.c \
ao_ms5607.c \
- ao_bmx160.c \
+ ao_bmi088.c \
+ ao_mmc5983.c \
ao_adxl375.c \
ao_adc_stm.c \
ao_beep_stm.c \
- ao_eeprom_stm.c \
ao_storage.c \
ao_m25.c \
ao_usb_stm.c \
ao_exti_stm.c \
ao_report.c \
- ao_i2c_stm.c \
ao_convert_pa.c \
ao_convert_volt.c \
ao_log.c \
PRODUCT_DEF=-DEASYMEGA
IDPRODUCT=0x0028
-CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF)
+CFLAGS = $(PRODUCT_DEF) $(STM32F1_CFLAGS)
PROGNAME=easymega-v3.0
PROG=$(PROGNAME)-$(VERSION).elf
#include <ao.h>
#include <ao_ms5607.h>
-#include <ao_bmx160.h>
+#include <ao_bmi088.h>
+#include <ao_mmc5983.h>
#include <ao_adxl375.h>
#include <ao_log.h>
#include <ao_exti.h>
#include <ao_companion.h>
-#include <ao_profile.h>
#include <ao_eeprom.h>
-#if HAS_SAMPLE_PROFILE
-#include <ao_sample_profile.h>
-#endif
#include <ao_pyro.h>
-#if HAS_STACK_GUARD
-#include <ao_mpu.h>
-#endif
int
main(void)
ao_cmd_init();
ao_ms5607_init();
- ao_bmx160_init();
+ ao_bmi088_init();
+ ao_mmc5983_init();
ao_adxl375_init();
- ao_eeprom_init();
ao_storage_init();
ao_flight_init();
#ifndef _AO_PINS_H_
#define _AO_PINS_H_
+/* 16MHz crystal */
-/* 16MHz High speed external crystal */
-#define AO_HSE 16000000
+#define AO_HSE 1
+#define AO_HSE_BYPASS 0
-/* PLLVCO = 96MHz (so that USB will work) */
-#define AO_PLLMUL 6
-#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_6)
+#define AO_SYSCLK 72000000
+#define AO_HCLK 72000000
+#define AO_APB1CLK 36000000
+#define AO_APB2CLK 72000000
+#define AO_ADCCLK 12000000
-/* SYSCLK = 32MHz (no need to go faster than CPU) */
-#define AO_PLLDIV 3
-#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3)
+/* PLLMUL is 9, PLLXTPRE (pre divider) is 2, so the
+ * overall PLLCLK is 16 * 9/2 = 72MHz (used as SYSCLK)
+ *
+ * HCLK is SYSCLK / 1 (HPRE_DIV) = 72MHz (72MHz max)
+ * USB is PLLCLK / 1.5 (USBPRE)= 48MHz (must be 48MHz)
+ * APB2 is HCLK / 1 (PPRE2_DIV) = 72MHz (72MHz max)
+ * APB1 is HCLK / 2 (PPRE1_DIV) = 36MHz (36MHz max)
+ * ADC is APB2 / 6 (ADCPRE) = 12MHz (14MHz max)
+ */
-/* HCLK = 32MHz (CPU clock) */
-#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_USBPRE STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE STM_RCC_CFGR_PLLXTPRE_2
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE1_DIV_2
#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 0
-#define USE_SERIAL_2_STDIN 0
-#define SERIAL_2_PA2_PA3 0
-#define SERIAL_2_PD5_PD6 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_serial1_getchar
-#define ao_gps_putchar ao_serial1_putchar
-#define ao_gps_set_speed ao_serial1_set_speed
-#define ao_gps_fifo (ao_stm_usart1.rx_fifo)
+#define AO_RCC_CFGR_ADCPRE STM_RCC_CFGR_ADCPRE_6
#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_EASYMEGA_2
+#define AO_LOG_FORMAT AO_LOG_FORMAT_EASYMEGA_3
+#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 USE_EEPROM_CONFIG 0
+#define USE_STORAGE_CONFIG 1
#define HAS_USB 1
#define HAS_BEEP 1
#define BEEPER_TIMER 2
#define HAS_APRS 0
#define HAS_COMPANION 1
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT (&stm_gpioa)
+#define AO_USB_PULLUP_PIN 8
+
#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 SPI_1_MODE_OUTPUT STM_GPIO_CR_MODE_OUTPUT_10MHZ
#define HAS_SPI_2 1
#define SPI_2_PB13_PB14_PB15 1 /* Flash, IMU, Companion */
-#define SPI_2_PD1_PD3_PD4 0
-#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz
+#define SPI_2_MODE_OUTPUT STM_GPIO_CR_MODE_OUTPUT_50MHZ
-#define HAS_I2C_1 1
-#define I2C_1_PB8_PB9 1
+#define HAS_I2C_1 0
+#define I2C_1_PB8_PB9 0
-#define HAS_I2C_2 0
-#define I2C_2_PB10_PB11 0
+#define HAS_I2C_2 1
+#define I2C_2_PB10_PB11 1
#define PACKET_HAS_SLAVE 0
#define PACKET_HAS_MASTER 0
#define LOW_LEVEL_DEBUG 0
-#define LED_PORT_ENABLE STM_RCC_AHBENR_GPIOAEN
-#define LED_PORT (&stm_gpioa)
-#define LED_PIN_RED 9
-#define LED_PIN_GREEN 10
-#define AO_LED_RED (1 << LED_PIN_RED)
-#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+#define LED_0_PORT (&stm_gpioa)
+#define LED_0_PIN 9
+#define LED_1_PORT (&stm_gpioa)
+#define LED_1_PIN 10
+#define AO_LED_RED (1 << LED_0_PIN)
+#define AO_LED_GREEN (1 << LED_1_PIN)
#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN)
/* Pyro C */
#define AO_PYRO_PORT_2 (&stm_gpiob)
-#define AO_PYRO_PIN_2 11
+#define AO_PYRO_PIN_2 9
/* Pyro D */
#define AO_PYRO_PORT_3 (&stm_gpiob)
-#define AO_PYRO_PIN_3 10
+#define AO_PYRO_PIN_3 8
/* Drogue */
#define AO_IGNITER_DROGUE_PORT (&stm_gpioa)
#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_M25_SPI_CS_MASK (1 << AO_M25_SPI_CS_PIN)
#define AO_M25_SPI_BUS AO_SPI_2_PB13_PB14_PB15
-/*
- * bmx160
- */
+/* BMI088 */
-#define HAS_BMX160 1
-#define AO_BMX160_INT_PORT (&stm_gpioc)
-#define AO_BMX160_INT_PIN 15
-#define AO_BMX160_SPI_BUS (AO_SPI_2_PB13_PB14_PB15 | AO_SPI_MODE_0)
-#define AO_BMX160_SPI_CS_PORT (&stm_gpioc)
-#define AO_BMX160_SPI_CS_PIN 13
+#define HAS_BMI088 1
+#define AO_BMI088_SPI_BUS (AO_SPI_2_PB13_PB14_PB15 | AO_SPI_MODE_0)
+#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_data_along(packet) ((packet)->bmx160.acc_x)
-#define ao_data_across(packet) (-(packet)->bmx160.acc_y)
-#define ao_data_through(packet) ((packet)->bmx160.acc_z)
+#define ao_bmi088_along(m) ((m)->acc.x)
+#define ao_bmi088_across(m) (-(m)->acc.y)
+#define ao_bmi088_through(m) ((m)->acc.z)
-#define ao_data_roll(packet) ((packet)->bmx160.gyr_x)
-#define ao_data_pitch(packet) (-(packet)->bmx160.gyr_y)
-#define ao_data_yaw(packet) ((packet)->bmx160.gyr_z)
+#define ao_bmi088_roll(m) ((m)->gyr.x)
+#define ao_bmi088_pitch(m) (-(m)->gyr.y)
+#define ao_bmi088_yaw(m) ((m)->gyr.z)
-#define ao_data_mag_along(packet) ((packet)->bmx160.mag_x)
-#define ao_data_mag_across(packet) (-(packet)->bmx160.mag_y)
-#define ao_data_mag_through(packet) ((packet)->bmx160.mag_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 MMC5983_I2C 1
+#define AO_MMC5983_I2C_INDEX STM_I2C_INDEX(2)
+
+#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 */
#define AO_ADXL375_CS_PORT (&stm_gpioc)
#define AO_ADXL375_CS_PIN 12
-#define AO_ADXL375_INT1_PORT (&stm_gpiob)
-#define AO_ADXL375_INT1_PIN 8
-
-#define AO_ADXL375_INT2_PORT (&stm_gpiob)
-#define AO_ADXL375_INT2_PIN 9
-
#define AO_ADXL375_AXIS x
#define AO_ADXL375_INVERT 1
TOPDIR=../..
HARDWARE=easymega-v3.0
-include $(TOPDIR)/stm/Makefile-flash.defs
+include $(TOPDIR)/stm32f1/Makefile-flash.defs
#ifndef _AO_PINS_H_
#define _AO_PINS_H_
-/* External crystal at 16MHz */
-#define AO_HSE 16000000
+/* 16MHz crystal */
+
+#define AO_HSE 1
+#define AO_HSE_BYPASS 0
+
+#define AO_SYSCLK 72000000
+#define AO_HCLK 72000000
+#define AO_APB1CLK 36000000
+#define AO_APB2CLK 72000000
+#define AO_ADCCLK 12000000
+
+/* PLLMUL is 9, PLLXTPRE (pre divider) is 2, so the
+ * overall PLLCLK is 16 * 9/2 = 72MHz (used as SYSCLK)
+ *
+ * HCLK is SYSCLK / 1 (HPRE_DIV) = 72MHz (72MHz max)
+ * USB is PLLCLK / 1.5 (USBPRE)= 48MHz (must be 48MHz)
+ * APB2 is HCLK / 1 (PPRE2_DIV) = 72MHz (72MHz max)
+ * APB1 is HCLK / 2 (PPRE1_DIV) = 36MHz (36MHz max)
+ * ADC is APB2 / 6 (ADCPRE) = 12MHz (14MHz max)
+ */
+
+#define AO_RCC_CFGR_USBPRE STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE STM_RCC_CFGR_PLLXTPRE_2
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE1_DIV_2
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+#define AO_RCC_CFGR_ADCPRE STM_RCC_CFGR_ADCPRE_6
#include <ao_flash_stm_pins.h>
#define AO_BOOT_APPLICATION_VALUE 1
#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT (&stm_gpioa)
+#define AO_USB_PULLUP_PIN 8
+
#endif /* _AO_PINS_H_ */
PROGNAME=easymini-v2.0
PROG=$(PROGNAME)-$(VERSION).elf
HEX=$(PROGNAME)-$(VERSION).ihx
+FLASH_PROG=flash-loader/$(PROGNAME)-altos-flash-$(VERSION).elf
+BOTH_DFU=$(PROGNAME)-combined-$(VERSION).dfu
+
+MAKEBIN=$(TOPDIR)/../ao-tools/ao-makebin/ao-makebin
SRC=$(ALTOS_SRC) ao_easymini.c
OBJ=$(SRC:.c=.o)
-all: $(PROG) $(HEX)
+all: $(PROG) $(HEX) $(BOTH_DFU)
$(PROG): Makefile $(OBJ)
$(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+$(BOTH_DFU): $(PROG) $(FLASH_PROG)
+ $(MAKEBIN) --dfu --output=$@ --base=$(FLASH_ADDR) $(FLASH_PROG) $(PROG)
+
+$(FLASH_PROG): FRC
+ +cd flash-loader && make
+
$(OBJ): $(INC)
distclean: clean
install:
uninstall:
+
+FRC:
--- /dev/null
+ao_product.h
+easytimer-*.elf
--- /dev/null
+#
+# AltOS build
+#
+#
+
+include ../samd21/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_boot.h \
+ ao_companion.h \
+ ao_data.h \
+ ao_sample.h \
+ ao_pins.h \
+ altitude-pa.h \
+ ao_kalman.h \
+ ao_product.h \
+ ao_task.h \
+ ao_whiten.h \
+ samd21.h \
+ Makefile
+
+SAMD21_ROM=128
+SAMD21_RAM=16
+
+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_stdio.c \
+ ao_storage.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_freq.c \
+ ao_dma_samd21.c \
+ ao_spi_samd21.c \
+ ao_data.c \
+ ao_bmi088.c \
+ ao_mmc5983.c \
+ ao_m25.c \
+ ao_adc_samd21.c \
+ ao_beep_samd21.c \
+ ao_usb_samd21.c \
+ ao_exti_samd21.c \
+ ao_convert_volt.c \
+ ao_report.c \
+ ao_sample.c \
+ ao_kalman.c \
+ ao_pyro.c \
+ ao_flight.c \
+ ao_ignite.c \
+ ao_log.c \
+ ao_log_timer.c \
+ $(PROFILE) \
+ $(SAMPLE_PROFILE) \
+ $(STACK_GUARD)
+
+PRODUCT=EasyTimer-v2
+PRODUCT_DEF=-DEASYTIMER_V_2
+IDPRODUCT=0x000d
+
+CFLAGS = $(PRODUCT_DEF) $(SAMD21_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF)
+
+PROGNAME=easytimer-v2
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+FLASH_PROG=flash-loader/$(PROGNAME)-altos-flash-$(VERSION).elf
+BOTH_HEX=$(PROGNAME)-combined-$(VERSION).ihx
+
+ELFTOHEX=$(TOPDIR)/../ao-tools/ao-elftohex/ao-elftohex
+
+SRC=$(ALTOS_SRC) ao_easytimer.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX) $(BOTH_HEX)
+
+$(PROG): Makefile $(OBJ)
+ $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(BOTH_HEX): $(PROG) $(FLASH_PROG)
+ $(ELFTOHEX) --nosym --output=$@ $(FLASH_PROG) $(PROG)
+
+$(OBJ): $(INC)
+
+
+$(FLASH_PROG): FRC
+ +cd flash-loader && make
+
+FRC:
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
+ rm -f ao_product.h
+
+install:
+
+uninstall:
--- /dev/null
+/*
+ * Copyright © 2016 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_ms5607.h>
+#include <ao_bmi088.h>
+#include <ao_mmc5983.h>
+#include <ao_log.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_dma_samd21.h>
+
+int
+main(void)
+{
+ ao_clock_init();
+
+#if HAS_STACK_GUARD
+ ao_mpu_init();
+#endif
+
+ ao_task_init();
+ ao_timer_init();
+
+ ao_dma_init();
+ ao_spi_init();
+ ao_exti_init();
+
+ ao_adc_init();
+ ao_beep_init();
+ ao_cmd_init();
+
+ ao_storage_init();
+
+ ao_bmi088_init();
+ ao_mmc5983_init();
+
+ ao_flight_init();
+ ao_log_init();
+ ao_report_init();
+
+ ao_usb_init();
+ ao_pyro_init();
+ ao_igniter_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
+/*
+ * 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; 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_
+
+#define AO_XOSC 1
+#define AO_XOSC_FREQ 16000000
+#define AO_XOSC_DIV 256
+#define AO_XOSC_MUL 768
+
+#define AO_AHB_PRESCALER 1
+#define AO_APBA_PRESCALER 1
+
+#define AO_CONFIG_MAX_SIZE 1024
+
+#define HAS_EEPROM 1
+#define USE_INTERNAL_FLASH 0
+#define USE_EEPROM_CONFIG 0
+#define USE_STORAGE_CONFIG 1
+#define HAS_USB 1
+#define HAS_BEEP 1
+#define HAS_BATTERY_REPORT 1
+#define AO_BEEP_TCC (&samd21_tcc2)
+#define AO_BEEP_TCC_APBC_MASK SAMD21_PM_APBCMASK_TCC2
+#define AO_BEEP_PORT (&samd21_port_a)
+#define AO_BEEP_PIN 16
+#define AO_BEEP_FUNC SAMD21_PORT_PMUX_FUNC_E
+#define HAS_RADIO 0
+#define HAS_TELEMETRY 0
+#define HAS_APRS 0
+#define HAS_COMPANION 0
+
+#define HAS_SPI_0 1
+#define HAS_SPI_3 1
+#define HAS_SPI_5 1
+
+#define LEDS_AVAILABLE 0
+
+#define HAS_GPS 0
+#define HAS_FLIGHT 1
+#define HAS_ADC 1
+#define HAS_ADC_TEMP 1
+#define HAS_LOG 1
+
+/*
+ * Igniter
+ */
+
+#define HAS_IGNITE 0
+#define HAS_IGNITE_REPORT 1
+#define AO_PYRO_NUM 2
+
+#define AO_SENSE_PYRO(p,n) ((p)->adc.sense[n])
+#define AO_IGNITER_CLOSED 400
+#define AO_IGNITER_OPEN 60
+
+/* Pyro A */
+#define AO_PYRO_PORT_0 (&samd21_port_a)
+#define AO_PYRO_PIN_0 1
+
+#define AO_ADC_SENSE_A 0
+#define AO_ADC_SENSE_A_PORT (&samd21_port_a)
+#define AO_ADC_SENSE_A_PIN 2
+
+/* Pyro B */
+#define AO_PYRO_PORT_1 (&samd21_port_b)
+#define AO_PYRO_PIN_1 9
+
+#define AO_ADC_SENSE_B 2
+#define AO_ADC_SENSE_B_PORT (&samd21_port_b)
+#define AO_ADC_SENSE_B_PIN 8
+
+
+/*
+ * ADC
+ */
+#define AO_DATA_RING 32
+#define AO_ADC_NUM_SENSE 2
+
+struct ao_adc {
+ int16_t sense[AO_ADC_NUM_SENSE];
+ int16_t v_batt;
+ int16_t temp;
+};
+
+#define AO_ADC_DUMP(p) \
+ printf("tick: %5lu A: %5d B: %5d batt: %5d\n", \
+ (p)->tick, \
+ (p)->adc.sense[0], (p)->adc.sense[1], \
+ (p)->adc.v_batt);
+
+#define AO_ADC_V_BATT 10
+#define AO_ADC_V_BATT_PORT (&samd21_port_b)
+#define AO_ADC_V_BATT_PIN 2
+
+#define AO_ADC_TEMP SAMD21_ADC_INPUTCTRL_MUXPOS_TEMP
+
+#define AO_NUM_ADC_PIN (AO_ADC_NUM_SENSE + 1)
+
+#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_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_SQ0 AO_ADC_SENSE_A
+#define AO_ADC_SQ1 AO_ADC_SENSE_B
+#define AO_ADC_SQ2 AO_ADC_V_BATT
+#define AO_ADC_SQ3 AO_ADC_TEMP
+
+/*
+ * Voltage divider on ADC battery sampler
+ */
+#define AO_BATTERY_DIV_PLUS 100 /* 100k */
+#define AO_BATTERY_DIV_MINUS 27 /* 27k */
+
+/*
+ * 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
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS 1
+#define AO_M25_SPI_CS_PORT (&samd21_port_b)
+#define AO_M25_SPI_CS_MASK (1 << 10)
+#define AO_M25_SPI_BUS AO_SPI_0_PA04_PA05_PA06
+
+/*
+ *
+ * Here are the required sensor signs:
+ *
+ * +along nose up
+ * +across switch screws down
+ * +through TH down
+ *
+ * With the board aligned to have positive accel for the relevant
+ * axis, looking down from above we have:
+ *
+ * +roll counter clockwise (nose up)
+ * +pitch counter clockwise (switch screws down)
+ * +yaw counter clockwise (TH down)
+ */
+
+
+/*
+ * On EasyTimer v2, bmi088 pin 1 (NE corner of chip) is placed away
+ * from the USB edge of the board. Relative to bmi088 specs, to get
+ * the above values, we need to flip the X and Y axes, assigning
+ * values as follows:
+ *
+ * +along -Y +roll -Y
+ * +across -X +pitch -X
+ * +through +Z +yaw +Z
+ */
+
+#define HAS_BMI088 1
+#define AO_BMI088_SPI_BUS AO_SPI_5_PB22_PB23_PB03
+#define AO_BMI088_ACC_CS_PORT (&samd21_port_a)
+#define AO_BMI088_ACC_CS_PIN 10
+#define AO_BMI088_GYR_CS_PORT (&samd21_port_a)
+#define AO_BMI088_GYR_CS_PIN 11
+#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 (&samd21_port_a)
+#define AO_MMC5983_INT_PIN 9
+#define AO_MMC5983_SPI_CLK_PORT (&samd21_port_a)
+#define AO_MMC5983_SPI_CLK_PIN 23
+#define AO_MMC5983_SPI_MISO_PORT (&samd21_port_a)
+#define AO_MMC5983_SPI_MISO_PIN 20
+#define AO_MMC5983_SPI_MOSI_PORT (&samd21_port_a)
+#define AO_MMC5983_SPI_MOSI_PIN 22
+#define AO_MMC5983_SPI_INDEX (AO_SPI_3_PA22_PA23_PA20 | AO_SPI_MODE_3)
+#define AO_MMC5983_SPI_CS_PORT (&samd21_port_a)
+#define AO_MMC5983_SPI_CS_PIN 8
+
+#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)
+
+/*
+ * Monitor
+ */
+
+#define HAS_MONITOR 0
+#define LEGACY_MONITOR 0
+#define HAS_MONITOR_PUT 1
+#define AO_MONITOR_LED 0
+#define HAS_RSSI 0
+
+/*
+ * Logging
+ */
+
+#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX (192 * 1024)
+#define AO_CONFIG_MAX_SIZE 1024
+#define LOG_ERASE_MARK 0x55
+#define LOG_MAX_ERASE 128
+#define AO_LOG_FORMAT AO_LOG_FORMAT_EASYTIMER_2
+#define AO_LOG_NORMALIZED 1
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=easytimer-v2
+include $(TOPDIR)/samd21/Makefile-flash.defs
--- /dev/null
+/*
+ * Copyright © 2024 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_
+
+#include <ao_flash_samd21_pins.h>
+
+/* Debug connector, pin 5 cs_companion0 PA18 */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO (samd21_port_a)
+#define AO_BOOT_APPLICATION_PIN 18
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE AO_MODE_PULL_UP
+
+/* USB */
+#define HAS_USB 1
+
+#define AO_XOSC 1
+#define AO_XOSC_FREQ 16000000
+#define AO_XOSC_DIV 256
+#define AO_XOSC_MUL 768
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+/*
+ * Copyright © 2018 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.
+ */
+
+#ifndef _AO_ADC_SINGLE_H_
+#define _AO_ADC_SINGLE_H_
+
+void
+ao_adc_single_get(struct ao_adc *packet);
+
+void
+ao_adc_single_init(void);
+
+#endif /* _AO_ADC_SINGLE_H_ */
#endif
+#if HAS_BARO
static void
ao_config_report_feet_show(void)
{
ao_config.report_feet = !!r;
_ao_config_edit_finish();
}
-
+#endif
#if HAS_BEEP
static void
{ "p <0 no limit, 1 limit>\0Limit radio power to 10mW",
ao_config_radio_10mw_set, ao_config_radio_10mw_show },
#endif
+#if HAS_BARO
{ "u <0 meters, 1 feet>\0Units to report height after landing",
ao_config_report_feet_set, ao_config_report_feet_show },
+#endif
{ "s\0Show",
ao_config_show, 0 },
#if HAS_CONFIG_SAVE
#endif
+#if !HAS_ACCEL && HAS_BMI088
+
+#define HAS_ACCEL 1
+
+typedef int16_t accel_t;
+
+#define ao_data_accel_raw(packet) -ao_data_along(packet)
+#define ao_data_accel_invert(a) (-(a))
+#define ao_data_accel_to_sample(accel) ao_bmi_accel_to_sample(accel)
+
+#endif
+
#if !HAS_GYRO && HAS_BMI088
#define HAS_GYRO 1
extra[i++] = (uint8_t) crc;
/* Append FEC -- 1 byte if odd, two bytes if even */
- num_fec = 2 - (i & 1);
+ num_fec = 2 - (len & 1);
while (num_fec--)
extra[i++] = AO_FEC_TRELLIS_TERMINATOR;
return i;
#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_EASYTIMER_2 23 /* 32 byte typed easytimer records with 32 bit gyro cal, bmi088 and mmc5983 */
+#define AO_LOG_FORMAT_EASYMEGA_3 24 /* 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 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_6
+struct ao_log_timer {
+ 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 */
+ int16_t ground_accel_along; /* 8 */
+ int16_t ground_accel_across; /* 10 */
+ int16_t ground_accel_through; /* 12 */
+ int16_t pad_14; /* 14 */
+ int32_t ground_roll; /* 16 */
+ int32_t ground_pitch; /* 20 */
+ int32_t ground_yaw; /* 24 */
+ } flight; /* 32 */
+ /* AO_LOG_STATE */
+ struct {
+ uint16_t state; /* 4 */
+ uint16_t reason; /* 6 */
+ } state;
+ /* AO_LOG_SENSOR */
+ struct {
+ int16_t accel_along; /* 4 */
+ int16_t accel_across; /* 6 */
+ int16_t accel_through; /* 8 */
+ int16_t gyro_roll; /* 10 */
+ int16_t gyro_pitch; /* 12 */
+ int16_t gyro_yaw; /* 14 */
+ int16_t mag_along; /* 16 */
+ int16_t mag_across; /* 18 */
+ int16_t mag_through; /* 20 */
+ int16_t v_batt; /* 22 */
+ int16_t v_pbatt; /* 24 */
+ int16_t sense[2]; /* 26 */
+ uint16_t pyro; /* 30 */
+ } sensor; /* 32 */
+ } 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 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_6 || AO_LOG_FORMAT == AO_LOG_FORMAT_EASYMEGA_3
typedef struct ao_log_mega ao_log_type;
#endif
#define AO_LOG_UNCOMMON 1
#endif
+#if AO_LOG_FORMAT == AO_LOG_FORMAT_EASYTIMER_2
+typedef struct ao_log_timer ao_log_type;
+#endif
+
#ifndef AO_LOG_UNCOMMON
extern ao_log_type ao_log_data;
--- /dev/null
+/*
+ * Copyright © 2024 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_log.h>
+#include <ao_data.h>
+#include <ao_flight.h>
+
+#if HAS_FLIGHT
+static uint8_t ao_log_data_pos;
+
+/* a hack to make sure that ao_log_timers fill the eeprom block in even units */
+typedef uint8_t check_log_size[1-(256 % sizeof(struct ao_log_timer))] ;
+
+#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)
+{
+ AO_TICK_TYPE next_sensor;
+ uint8_t i;
+
+ ao_storage_setup();
+
+ ao_log_scan();
+
+ while (!ao_log_running)
+ ao_sleep(&ao_log_running);
+
+#if HAS_FLIGHT
+ ao_log_data.type = AO_LOG_FLIGHT;
+ ao_log_data.tick = (uint16_t) ao_sample_tick;
+#if HAS_ACCEL
+ ao_log_data.u.flight.ground_accel = ao_ground_accel;
+#endif
+#if HAS_GYRO
+ ao_log_data.u.flight.ground_accel_along = ao_ground_accel_along;
+ ao_log_data.u.flight.ground_accel_across = ao_ground_accel_across;
+ ao_log_data.u.flight.ground_accel_through = ao_ground_accel_through;
+ ao_log_data.u.flight.ground_roll = ao_ground_roll;
+ ao_log_data.u.flight.ground_pitch = ao_ground_pitch;
+ ao_log_data.u.flight.ground_yaw = ao_ground_yaw;
+#endif
+ ao_log_data.u.flight.flight = ao_flight_number;
+ ao_log_write(&ao_log_data);
+#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_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) {
+ AO_TICK_TYPE tick = ao_data_ring[ao_log_data_pos].tick;
+ ao_log_data.tick = (uint16_t) tick;
+ volatile struct ao_data *d = &ao_data_ring[ao_log_data_pos];
+ if ((AO_TICK_SIGNED) (tick - next_sensor) >= 0) {
+ ao_log_data.type = AO_LOG_SENSOR;
+
+#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);
+#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
+ ao_log_data.u.sensor.v_batt = d->adc.v_batt;
+ for (i = 0; i < AO_ADC_NUM_SENSE; i++)
+ ao_log_data.u.sensor.sense[i] = d->adc.sense[i];
+ ao_log_data.u.sensor.pyro = ao_pyro_fired;
+
+ ao_log_write(&ao_log_data);
+ if (ao_log_state <= ao_flight_coast)
+ next_sensor = tick + AO_SENSOR_INTERVAL_ASCENT;
+ else
+ next_sensor = tick + AO_SENSOR_INTERVAL_DESCENT;
+ }
+ 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;
+ ao_log_data.type = AO_LOG_STATE;
+ ao_log_data.tick = (uint16_t) ao_time();
+ ao_log_data.u.state.state = ao_log_state;
+ ao_log_data.u.state.reason = 0;
+ ao_log_write(&ao_log_data);
+
+ 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 /* HAS_FLIGHT */
+
for (v = 0; v < NUM_PYRO_VALUES; v++)
{
- value = ao_pyro_get_1_24(&pyro_1_24[p], ao_pyro_values[v].flag);
- ao_pyro_put(&tmp, ao_pyro_values[v].offset,
- ao_pyro_size(ao_pyro_values[v].flag), value);
+ if (ao_pyro_values[v].offset != NO_VALUE) {
+ value = ao_pyro_get_1_24(&pyro_1_24[p], ao_pyro_values[v].flag);
+ ao_pyro_put(&tmp, ao_pyro_values[v].offset,
+ ao_pyro_size(ao_pyro_values[v].flag), value);
+ }
}
memcpy(&pyro_1_25[p], &tmp, sizeof(tmp));
}
/* ADC clock is divided by this value + 1, which ensures that
* the ADC clock will be strictly less than 4.5MHz as required
*/
-#define AO_ADC_CLKDIV (AO_LPC_SYSCLK / 450000)
+#ifndef AO_LPC_ADC_CLOCK
+#define AO_LPC_ADC_CLOCK 4500000
+#endif
+#define AO_ADC_CLKDIV (AO_LPC_SYSCLK / AO_LPC_ADC_CLOCK)
static uint8_t ao_adc_ready;
static uint8_t ao_adc_sequence;
#define _AO_SPI_SPEED_62500Hz 768
static inline uint32_t
-ao_spi_speed(uint32_t hz)
+ao_spi_speed(int index, uint32_t hz)
{
+ (void) index;
if (hz >= 4000000) return _AO_SPI_SPEED_4MHz;
if (hz >= 2000000) return _AO_SPI_SPEED_2MHz;
if (hz >= 1000000) return _AO_SPI_SPEED_1MHz;
}
static inline uint8_t
-ao_spi_speed(uint32_t hz)
+ao_spi_speed(int index, uint32_t hz)
{
int32_t baud = (int32_t) (AO_SYSCLK / (2 * hz)) - 1;
+ (void) index;
if (baud < 1)
baud = 1;
if (baud > 255)
#define AO_USB_DEVICE_ID_SERIAL 0
#endif
+#ifndef USE_USB_STDIO
+#define USE_USB_STDIO 1
+#endif
+
#if USE_USB_FIFO
static struct ao_fifo ao_usb_rx_fifo;
#endif
#define SNEK_CS_PORT (&samd21_port_a)
#define SNEK_CS_PIN (11)
#define SNEK_SPI_INDEX AO_SPI_0_PA08_PA09_PA10
-#define SNEK_SPI_SPEED ao_spi_speed(1000000)
+#define SNEK_SPI_SPEED ao_spi_speed(SNEK_SPI_INDEX, 1000000)
static const uint8_t spi_test[] = {
0x55,
+++ /dev/null
-/*
- * Copyright © 2018 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.
- */
-
-#ifndef _AO_ADC_SINGLE_H_
-#define _AO_ADC_SINGLE_H_
-
-void
-ao_adc_single_get(struct ao_adc *packet);
-
-void
-ao_adc_single_init(void);
-
-#endif /* _AO_ADC_SINGLE_H_ */
#define _AO_SPI_SPEED_62500Hz STM_SPI_CR1_BR_PCLK_256
static inline uint32_t
-ao_spi_speed(uint32_t hz)
+ao_spi_speed(int index, uint32_t hz)
{
+ (void) index;
if (hz >= 4000000) return _AO_SPI_SPEED_4MHz;
if (hz >= 2000000) return _AO_SPI_SPEED_2MHz;
if (hz >= 1000000) return _AO_SPI_SPEED_1MHz;
--- /dev/null
+include $(TOPDIR)/stm32f1/Makefile-stm32f1.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_flash_pins.h \
+ ao_flash_stm_pins.h \
+ ao_flash_task.h \
+ ao_pins.h \
+ ao_product.h \
+ Makefile
+
+#
+# Common AltOS sources
+#
+SRC = \
+ ao_interrupt.c \
+ ao_romconfig.c \
+ ao_boot_chain.c \
+ ao_boot_pin.c \
+ ao_product.c \
+ ao_notask.c \
+ ao_clock.c \
+ ao_usb_stm.c \
+ ao_flash_stm.c \
+ ao_flash_task.c \
+ ao_flash_loader_stm.c
+
+OBJ=$(SRC:.c=.o)
+
+PRODUCT=AltosFlash
+PRODUCT_DEF=-DALTOS_FLASH
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(STM32F1_CFLAGS)
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f1 -Taltos-loader.ld -n -Wl,--gc-sections
+
+PROGNAME=$(HARDWARE)-altos-flash
+PROG=$(PROGNAME)-$(VERSION).elf
+
+$(PROG): Makefile $(OBJ) altos-loader.ld
+ $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+all: $(PROG)
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.map
+ rm -f ao_product.h
+
+install:
+
+uninstall:
--- /dev/null
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/stm32f1/Makefile-stm32f1.defs
+
+LOADER=flash-loader/$(PROGNAME)-altos-flash-$(VERSION).elf
+MAKEBIN=$(TOPDIR)/../ao-tools/ao-makebin/ao-makebin
+FLASH_ADDR=0x08000000
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f1 -Taltos-raw.ld -n
+
+.DEFAULT_GOAL=all
--- /dev/null
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/Makefile.defs
+
+vpath % $(TOPDIR)/stm32f1:$(AO_VPATH)
+
+CC=$(ARM_CC)
+
+STM32F1_CFLAGS=-mlittle-endian -mcpu=cortex-m3 -mthumb \
+ -I$(TOPDIR)/stm32f1 $(AO_CFLAGS) $(PICOLIBC_CFLAGS)
--- /dev/null
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/stm32f1/Makefile-stm32f1.defs
+
+STM32F1_LINKER_SCRIPT?=altos.ld
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f1 -T$(STM32F1_LINKER_SCRIPT) -n -Wl,--gc-sections
+
+.DEFAULT_GOAL=all
--- /dev/null
+/*
+ * Copyright © 2024 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.
+ */
+
+__flash = 0x08001000;
+__eeprom_base = 0x0801f800;
+__flash_size = 122K;
+__ram = 0x20000000;
+__ram_size = 20k;
+__stack_size = 512;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
--- /dev/null
+/*
+ * Copyright © 2018 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.
+ */
+
+__flash = 0x08000000;
+__flash_size = 4k;
+__ram = 0x20000000;
+__ram_size = 20k;
+__stack_size = 512;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
--- /dev/null
+__flash = 0x20000000;
+__flash_size = 12k;
+__ram = 0x20003000;
+__ram_size = 8k;
+__stack_size = 512;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
--- /dev/null
+/*
+ * Copyright © 2018 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.
+ */
+
+__flash = 0x08000000;
+__flash_size = 64k;
+__ram = 0x20000000;
+__ram_size = 20k;
+__stack_size = 512;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
--- /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; 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.
+ */
+
+__flash = 0x08001000;
+__eeprom_base = 0x0800e800;
+__flash_size = 58K;
+__ram = 0x20000000;
+__ram_size = 20k;
+__stack_size = 512;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
--- /dev/null
+/*
+ * Copyright © 2024 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_data.h>
+#include <ao_adc_single.h>
+
+static uint8_t ao_adc_ready;
+
+#define AO_ADC_CR2_VAL(start) ((HAS_ADC_TEMP << STM_ADC_CR2_TSVREFE) |\
+ ((start) << STM_ADC_CR2_SWSTART) | \
+ (0 << STM_ADC_CR2_JWSTART) | \
+ (0 << STM_ADC_CR2_EXTTRIG) | \
+ (STM_ADC_CR2_EXTSEL_SWSTART << STM_ADC_CR2_EXTSEL) | \
+ (0 << STM_ADC_CR2_JEXTTRIG) | \
+ (0 << STM_ADC_CR2_JEXTSEL) | \
+ (0 << STM_ADC_CR2_ALIGN) | \
+ (1 << STM_ADC_CR2_DMA) | \
+ (0 << STM_ADC_CR2_CONT) | \
+ (1 << STM_ADC_CR2_ADON))
+
+/*
+ * Callback from DMA ISR
+ *
+ * Shut down DMA engine, signal anyone waiting
+ */
+static void ao_adc_done(int index)
+{
+ (void) index;
+ ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+ ao_adc_ready = 1;
+ /* Turn the ADC back off */
+ stm_adc1.cr2 = 0;
+ ao_wakeup((void *) &ao_adc_ready);
+}
+
+/*
+ * Start the ADC sequence using the DMA engine
+ */
+static void
+ao_adc_poll(struct ao_adc *packet)
+{
+ ao_adc_ready = 0;
+ stm_adc1.sr = 0;
+ ao_dma_set_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1),
+ &stm_adc1.dr,
+ (void *) packet,
+ AO_NUM_ADC,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) |
+ (1 << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+ ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1), ao_adc_done);
+ ao_dma_start(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+
+ stm_adc1.cr2 = AO_ADC_CR2_VAL(0);
+ ao_delay(AO_MS_TO_TICKS(10));
+ stm_adc1.cr2 = AO_ADC_CR2_VAL(0);
+ ao_delay(AO_MS_TO_TICKS(10));
+ stm_adc1.cr2 = AO_ADC_CR2_VAL(1);
+}
+
+/*
+ * Fetch a copy of the most recent ADC data
+ */
+void
+ao_adc_single_get(struct ao_adc *packet)
+{
+ ao_adc_poll(packet);
+ ao_arch_block_interrupts();
+ while (!ao_adc_ready)
+ ao_sleep(&ao_adc_ready);
+ ao_arch_release_interrupts();
+}
+
+static void
+ao_adc_dump(void)
+{
+ struct ao_adc packet;
+ ao_adc_single_get(&packet);
+ AO_ADC_DUMP(&packet);
+}
+
+const struct ao_cmds ao_adc_cmds[] = {
+ { ao_adc_dump, "a\0Display current ADC values" },
+ { 0, NULL },
+};
+
+static inline void
+adc_pin_set(struct stm_gpio *gpio, int pin)
+{
+ ao_enable_port(gpio);
+ stm_gpio_conf(gpio, pin,
+ STM_GPIO_CR_MODE_INPUT,
+ STM_GPIO_CR_CNF_INPUT_ANALOG);
+}
+
+void
+ao_adc_single_init(void)
+{
+#ifdef AO_ADC_PIN0_PORT
+ adc_pin_set(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN);
+#endif
+#ifdef AO_ADC_PIN1_PORT
+ adc_pin_set(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN);
+#endif
+#ifdef AO_ADC_PIN2_PORT
+ adc_pin_set(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN);
+#endif
+#ifdef AO_ADC_PIN3_PORT
+ adc_pin_set(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN);
+#endif
+#ifdef AO_ADC_PIN4_PORT
+ adc_pin_set(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN);
+#endif
+#ifdef AO_ADC_PIN5_PORT
+ adc_pin_set(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN);
+#endif
+#ifdef AO_ADC_PIN6_PORT
+ adc_pin_set(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN);
+#endif
+#ifdef AO_ADC_PIN7_PORT
+ adc_pin_set(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN);
+#endif
+#ifdef AO_ADC_PIN8_PORT
+ adc_pin_set(AO_ADC_PIN8_PORT, AO_ADC_PIN8_PIN);
+#endif
+#ifdef AO_ADC_PIN9_PORT
+ adc_pin_set(AO_ADC_PIN9_PORT, AO_ADC_PIN9_PIN);
+#endif
+#ifdef AO_ADC_PIN10_PORT
+ adc_pin_set(AO_ADC_PIN10_PORT, AO_ADC_PIN10_PIN);
+#endif
+#ifdef AO_ADC_PIN11_PORT
+ adc_pin_set(AO_ADC_PIN11_PORT, AO_ADC_PIN11_PIN);
+#endif
+#ifdef AO_ADC_PIN12_PORT
+ adc_pin_set(AO_ADC_PIN12_PORT, AO_ADC_PIN12_PIN);
+#endif
+#ifdef AO_ADC_PIN13_PORT
+ adc_pin_set(AO_ADC_PIN13_PORT, AO_ADC_PIN13_PIN);
+#endif
+#ifdef AO_ADC_PIN14_PORT
+ adc_pin_set(AO_ADC_PIN14_PORT, AO_ADC_PIN14_PIN);
+#endif
+#ifdef AO_ADC_PIN15_PORT
+ adc_pin_set(AO_ADC_PIN15_PORT, AO_ADC_PIN15_PIN);
+#endif
+#ifdef AO_ADC_PIN16_PORT
+ adc_pin_set(AO_ADC_PIN16_PORT, AO_ADC_PIN16_PIN);
+#endif
+#ifdef AO_ADC_PIN17_PORT
+ adc_pin_set(AO_ADC_PIN17_PORT, AO_ADC_PIN17_PIN);
+#endif
+#ifdef AO_ADC_PIN18_PORT
+ adc_pin_set(AO_ADC_PIN18_PORT, AO_ADC_PIN18_PIN);
+#endif
+#ifdef AO_ADC_PIN19_PORT
+ adc_pin_set(AO_ADC_PIN19_PORT, AO_ADC_PIN19_PIN);
+#endif
+#ifdef AO_ADC_PIN20_PORT
+ adc_pin_set(AO_ADC_PIN20_PORT, AO_ADC_PIN20_PIN);
+#endif
+#ifdef AO_ADC_PIN21_PORT
+ adc_pin_set(AO_ADC_PIN21_PORT, AO_ADC_PIN21_PIN);
+#endif
+#ifdef AO_ADC_PIN22_PORT
+ adc_pin_set(AO_ADC_PIN22_PORT, AO_ADC_PIN22_PIN);
+#endif
+#ifdef AO_ADC_PIN23_PORT
+ adc_pin_set(AO_ADC_PIN23_PORT, AO_ADC_PIN23_PIN);
+#endif
+#ifdef AO_ADC_PIN24_PORT
+ #error "Too many ADC ports"
+#endif
+
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_ADC1EN);
+
+ /* Turn off ADC during configuration */
+ stm_adc1.cr2 = 0;
+
+ stm_adc1.cr1 = ((0 << STM_ADC_CR1_AWDEN ) |
+ (0 << STM_ADC_CR1_JAWDEN ) |
+ (STM_ADC_CR1_DUALMOD_INDEPENDENT << STM_ADC_CR1_DUALMOD ) |
+ (0 << STM_ADC_CR1_DISCNUM ) |
+ (0 << STM_ADC_CR1_JDISCEN ) |
+ (0 << STM_ADC_CR1_DISCEN ) |
+ (0 << STM_ADC_CR1_JAUTO ) |
+ (0 << STM_ADC_CR1_AWDSGL ) |
+ (1 << STM_ADC_CR1_SCAN ) |
+ (0 << STM_ADC_CR1_JEOCIE ) |
+ (0 << STM_ADC_CR1_AWDIE ) |
+ (0 << STM_ADC_CR1_EOCIE ) |
+ (0 << STM_ADC_CR1_AWDCH ));
+
+ /* 384 cycle sample time for everyone */
+ stm_adc1.smpr1 = 0x00ffffff;
+ stm_adc1.smpr2 = 0x3fffffff;
+
+ stm_adc1.sqr1 = ((AO_NUM_ADC - 1) << 20);
+#if AO_NUM_ADC > 0
+ stm_adc1.sqr3 |= (AO_ADC_SQ1 << 0);
+#endif
+#if AO_NUM_ADC > 1
+ stm_adc1.sqr3 |= (AO_ADC_SQ2 << 5);
+#endif
+#if AO_NUM_ADC > 2
+ stm_adc1.sqr3 |= (AO_ADC_SQ3 << 10);
+#endif
+#if AO_NUM_ADC > 3
+ stm_adc1.sqr3 |= (AO_ADC_SQ4 << 15);
+#endif
+#if AO_NUM_ADC > 4
+ stm_adc1.sqr3 |= (AO_ADC_SQ5 << 20);
+#endif
+#if AO_NUM_ADC > 5
+ stm_adc1.sqr3 |= (AO_ADC_SQ6 << 25);
+#endif
+#if AO_NUM_ADC > 6
+ stm_adc1.sqr2 |= (AO_ADC_SQ7 << 0);
+#endif
+#if AO_NUM_ADC > 7
+ stm_adc1.sqr2 |= (AO_ADC_SQ8 << 5);
+#endif
+#if AO_NUM_ADC > 8
+ stm_adc1.sqr2 |= (AO_ADC_SQ9 << 10);
+#endif
+#if AO_NUM_ADC > 9
+ stm_adc1.sqr2 |= (AO_ADC_SQ10 << 15);
+#endif
+#if AO_NUM_ADC > 10
+ stm_adc1.sqr2 |= (AO_ADC_SQ11 << 20);
+#endif
+#if AO_NUM_ADC > 11
+ stm_adc1.sqr2 |= (AO_ADC_SQ12 << 25);
+#endif
+#if AO_NUM_ADC > 12
+ stm_adc1.sqr1 |= (AO_ADC_SQ13 << 0);
+#endif
+#if AO_NUM_ADC > 13
+ stm_adc1.sqr1 |= (AO_ADC_SQ14 << 5);
+#endif
+#if AO_NUM_ADC > 14
+ stm_adc1.sqr1 |= (AO_ADC_SQ15 << 10);
+#endif
+#if AO_NUM_ADC > 15
+ stm_adc1.sqr1 |= (AO_ADC_SQ16 << 15);
+#endif
+#if AO_NUM_ADC > 15
+#error "too many ADC channels"
+#endif
+
+ /* Clear any stale status bits */
+ stm_adc1.sr = 0;
+
+ ao_dma_alloc(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+
+ ao_cmd_register(&ao_adc_cmds[0]);
+}
--- /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; 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_data.h>
+
+static uint8_t ao_adc_ready;
+
+#define AO_ADC_CR2_VAL(start) ((HAS_ADC_TEMP << STM_ADC_CR2_TSVREFE) |\
+ ((start) << STM_ADC_CR2_SWSTART) | \
+ (0 << STM_ADC_CR2_JWSTART) | \
+ (0 << STM_ADC_CR2_EXTTRIG) | \
+ (STM_ADC_CR2_EXTSEL_SWSTART << STM_ADC_CR2_EXTSEL) | \
+ (0 << STM_ADC_CR2_JEXTTRIG) | \
+ (0 << STM_ADC_CR2_JEXTSEL) | \
+ (0 << STM_ADC_CR2_ALIGN) | \
+ (1 << STM_ADC_CR2_DMA) | \
+ (0 << STM_ADC_CR2_CONT) | \
+ (1 << STM_ADC_CR2_ADON))
+
+/*
+ * Callback from DMA ISR
+ *
+ * Mark time in ring, shut down DMA engine
+ */
+static void ao_adc_done(int index)
+{
+ (void) index;
+ AO_DATA_PRESENT(AO_DATA_ADC);
+ ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+ ao_data_fill(ao_data_head);
+ ao_adc_ready = 1;
+}
+
+/*
+ * Start the ADC sequence using the DMA engine
+ */
+void
+ao_adc_poll(void)
+{
+ if (!ao_adc_ready)
+ return;
+ ao_adc_ready = 0;
+ stm_adc1.sr = 0;
+ ao_dma_set_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1),
+ &stm_adc1.dr,
+ (void *) (&ao_data_ring[ao_data_head].adc),
+ AO_NUM_ADC,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) |
+ (1 << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+ ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1), ao_adc_done);
+ ao_dma_start(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+
+ stm_adc1.cr2 = AO_ADC_CR2_VAL(0);
+ stm_adc1.cr2 = AO_ADC_CR2_VAL(0);
+ stm_adc1.cr2 = AO_ADC_CR2_VAL(1);
+}
+
+#ifdef AO_ADC_SQ1_NAME
+static const char *ao_adc_name[AO_NUM_ADC] = {
+ AO_ADC_SQ1_NAME,
+#ifdef AO_ADC_SQ2_NAME
+ AO_ADC_SQ2_NAME,
+#endif
+#ifdef AO_ADC_SQ3_NAME
+ AO_ADC_SQ3_NAME,
+#endif
+#ifdef AO_ADC_SQ4_NAME
+ AO_ADC_SQ4_NAME,
+#endif
+#ifdef AO_ADC_SQ5_NAME
+ AO_ADC_SQ5_NAME,
+#endif
+#ifdef AO_ADC_SQ6_NAME
+ AO_ADC_SQ6_NAME,
+#endif
+#ifdef AO_ADC_SQ7_NAME
+ AO_ADC_SQ7_NAME,
+#endif
+#ifdef AO_ADC_SQ8_NAME
+ AO_ADC_SQ8_NAME,
+#endif
+#ifdef AO_ADC_SQ9_NAME
+ AO_ADC_SQ9_NAME,
+#endif
+#ifdef AO_ADC_SQ10_NAME
+ AO_ADC_SQ10_NAME,
+#endif
+#ifdef AO_ADC_SQ11_NAME
+ AO_ADC_SQ11_NAME,
+#endif
+#ifdef AO_ADC_SQ12_NAME
+ AO_ADC_SQ12_NAME,
+#endif
+#ifdef AO_ADC_SQ13_NAME
+ AO_ADC_SQ13_NAME,
+#endif
+#ifdef AO_ADC_SQ14_NAME
+ AO_ADC_SQ14_NAME,
+#endif
+#ifdef AO_ADC_SQ15_NAME
+ AO_ADC_SQ15_NAME,
+#endif
+#ifdef AO_ADC_SQ16_NAME
+ AO_ADC_SQ16_NAME,
+#endif
+#ifdef AO_ADC_SQ17_NAME
+ AO_ADC_SQ17_NAME,
+#endif
+#ifdef AO_ADC_SQ18_NAME
+ AO_ADC_SQ18_NAME,
+#endif
+#ifdef AO_ADC_SQ19_NAME
+ AO_ADC_SQ19_NAME,
+#endif
+#ifdef AO_ADC_SQ20_NAME
+ AO_ADC_SQ20_NAME,
+#endif
+#ifdef AO_ADC_SQ21_NAME
+ #error "too many ADC names"
+#endif
+};
+#endif
+
+static void
+ao_adc_dump(void)
+{
+ struct ao_data packet;
+#ifndef AO_ADC_DUMP
+ uint8_t i;
+ int16_t *d;
+#endif
+
+ ao_data_get(&packet);
+#ifdef AO_ADC_DUMP
+ AO_ADC_DUMP(&packet);
+#else
+ printf("tick: %5u", packet.tick);
+ d = (int16_t *) (&packet.adc);
+ for (i = 0; i < AO_NUM_ADC; i++) {
+#ifdef AO_ADC_SQ1_NAME
+ if (ao_adc_name[i])
+ printf (" %s: %5d", ao_adc_name[i], d[i]);
+ else
+#endif
+ printf (" %2d: %5d", i, d[i]);
+ }
+ printf("\n");
+#endif
+}
+
+const struct ao_cmds ao_adc_cmds[] = {
+ { ao_adc_dump, "a\0Display current ADC values" },
+ { 0, NULL },
+};
+
+static inline void
+adc_pin_set(struct stm_gpio *gpio, int pin)
+{
+ ao_enable_port(gpio);
+ stm_gpio_conf(gpio, pin,
+ STM_GPIO_CR_MODE_INPUT,
+ STM_GPIO_CR_CNF_INPUT_ANALOG);
+}
+
+void
+ao_adc_init(void)
+{
+#ifdef AO_ADC_PIN0_PORT
+ adc_pin_set(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN);
+#endif
+#ifdef AO_ADC_PIN1_PORT
+ adc_pin_set(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN);
+#endif
+#ifdef AO_ADC_PIN2_PORT
+ adc_pin_set(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN);
+#endif
+#ifdef AO_ADC_PIN3_PORT
+ adc_pin_set(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN);
+#endif
+#ifdef AO_ADC_PIN4_PORT
+ adc_pin_set(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN);
+#endif
+#ifdef AO_ADC_PIN5_PORT
+ adc_pin_set(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN);
+#endif
+#ifdef AO_ADC_PIN6_PORT
+ adc_pin_set(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN);
+#endif
+#ifdef AO_ADC_PIN7_PORT
+ adc_pin_set(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN);
+#endif
+#ifdef AO_ADC_PIN8_PORT
+ adc_pin_set(AO_ADC_PIN8_PORT, AO_ADC_PIN8_PIN);
+#endif
+#ifdef AO_ADC_PIN9_PORT
+ adc_pin_set(AO_ADC_PIN9_PORT, AO_ADC_PIN9_PIN);
+#endif
+#ifdef AO_ADC_PIN10_PORT
+ adc_pin_set(AO_ADC_PIN10_PORT, AO_ADC_PIN10_PIN);
+#endif
+#ifdef AO_ADC_PIN11_PORT
+ adc_pin_set(AO_ADC_PIN11_PORT, AO_ADC_PIN11_PIN);
+#endif
+#ifdef AO_ADC_PIN12_PORT
+ adc_pin_set(AO_ADC_PIN12_PORT, AO_ADC_PIN12_PIN);
+#endif
+#ifdef AO_ADC_PIN13_PORT
+ adc_pin_set(AO_ADC_PIN13_PORT, AO_ADC_PIN13_PIN);
+#endif
+#ifdef AO_ADC_PIN14_PORT
+ adc_pin_set(AO_ADC_PIN14_PORT, AO_ADC_PIN14_PIN);
+#endif
+#ifdef AO_ADC_PIN15_PORT
+ adc_pin_set(AO_ADC_PIN15_PORT, AO_ADC_PIN15_PIN);
+#endif
+#ifdef AO_ADC_PIN16_PORT
+ adc_pin_set(AO_ADC_PIN16_PORT, AO_ADC_PIN16_PIN);
+#endif
+#ifdef AO_ADC_PIN17_PORT
+ adc_pin_set(AO_ADC_PIN17_PORT, AO_ADC_PIN17_PIN);
+#endif
+#ifdef AO_ADC_PIN18_PORT
+ adc_pin_set(AO_ADC_PIN18_PORT, AO_ADC_PIN18_PIN);
+#endif
+#ifdef AO_ADC_PIN19_PORT
+ adc_pin_set(AO_ADC_PIN19_PORT, AO_ADC_PIN19_PIN);
+#endif
+#ifdef AO_ADC_PIN20_PORT
+ adc_pin_set(AO_ADC_PIN20_PORT, AO_ADC_PIN20_PIN);
+#endif
+#ifdef AO_ADC_PIN21_PORT
+ adc_pin_set(AO_ADC_PIN21_PORT, AO_ADC_PIN21_PIN);
+#endif
+#ifdef AO_ADC_PIN22_PORT
+ adc_pin_set(AO_ADC_PIN22_PORT, AO_ADC_PIN22_PIN);
+#endif
+#ifdef AO_ADC_PIN23_PORT
+ adc_pin_set(AO_ADC_PIN23_PORT, AO_ADC_PIN23_PIN);
+#endif
+#ifdef AO_ADC_PIN24_PORT
+ #error "Too many ADC ports"
+#endif
+
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_ADC1EN);
+
+ /* Turn off ADC during configuration */
+ stm_adc1.cr2 = 0;
+
+ stm_adc1.cr1 = ((0 << STM_ADC_CR1_AWDEN ) |
+ (0 << STM_ADC_CR1_JAWDEN ) |
+ (STM_ADC_CR1_DUALMOD_INDEPENDENT << STM_ADC_CR1_DUALMOD ) |
+ (0 << STM_ADC_CR1_DISCNUM ) |
+ (0 << STM_ADC_CR1_JDISCEN ) |
+ (0 << STM_ADC_CR1_DISCEN ) |
+ (0 << STM_ADC_CR1_JAUTO ) |
+ (0 << STM_ADC_CR1_AWDSGL ) |
+ (1 << STM_ADC_CR1_SCAN ) |
+ (0 << STM_ADC_CR1_JEOCIE ) |
+ (0 << STM_ADC_CR1_AWDIE ) |
+ (0 << STM_ADC_CR1_EOCIE ) |
+ (0 << STM_ADC_CR1_AWDCH ));
+
+ /* 384 cycle sample time for everyone */
+ stm_adc1.smpr1 = 0x3ffff;
+ stm_adc1.smpr2 = 0x3fffffff;
+
+ stm_adc1.sqr1 = ((AO_NUM_ADC - 1) << 20);
+#if AO_NUM_ADC > 0
+ stm_adc1.sqr3 |= (AO_ADC_SQ1 << 0);
+#endif
+#if AO_NUM_ADC > 1
+ stm_adc1.sqr3 |= (AO_ADC_SQ2 << 5);
+#endif
+#if AO_NUM_ADC > 2
+ stm_adc1.sqr3 |= (AO_ADC_SQ3 << 10);
+#endif
+#if AO_NUM_ADC > 3
+ stm_adc1.sqr3 |= (AO_ADC_SQ4 << 15);
+#endif
+#if AO_NUM_ADC > 4
+ stm_adc1.sqr3 |= (AO_ADC_SQ5 << 20);
+#endif
+#if AO_NUM_ADC > 5
+ stm_adc1.sqr3 |= (AO_ADC_SQ6 << 25);
+#endif
+#if AO_NUM_ADC > 6
+ stm_adc1.sqr2 |= (AO_ADC_SQ7 << 0);
+#endif
+#if AO_NUM_ADC > 7
+ stm_adc1.sqr2 |= (AO_ADC_SQ8 << 5);
+#endif
+#if AO_NUM_ADC > 8
+ stm_adc1.sqr2 |= (AO_ADC_SQ9 << 10);
+#endif
+#if AO_NUM_ADC > 9
+ stm_adc1.sqr2 |= (AO_ADC_SQ10 << 15);
+#endif
+#if AO_NUM_ADC > 10
+ stm_adc1.sqr2 |= (AO_ADC_SQ11 << 20);
+#endif
+#if AO_NUM_ADC > 11
+ stm_adc1.sqr2 |= (AO_ADC_SQ12 << 25);
+#endif
+#if AO_NUM_ADC > 12
+ stm_adc1.sqr1 |= (AO_ADC_SQ13 << 0);
+#endif
+#if AO_NUM_ADC > 13
+ stm_adc1.sqr1 |= (AO_ADC_SQ14 << 5);
+#endif
+#if AO_NUM_ADC > 14
+ stm_adc1.sqr1 |= (AO_ADC_SQ15 << 10);
+#endif
+#if AO_NUM_ADC > 15
+ stm_adc1.sqr1 |= (AO_ADC_SQ16 << 15);
+#endif
+#if AO_NUM_ADC > 15
+#error "too many ADC channels"
+#endif
+
+#ifndef HAS_ADC_TEMP
+#error Please define HAS_ADC_TEMP
+#endif
+#if HAS_ADC_TEMP
+ stm_adc1.cr2 |= ((1 << STM_ADC_CR2_TSVREFE));
+#endif
+
+ /* Clear any stale status bits */
+ stm_adc1.sr = 0;
+
+ ao_dma_alloc(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+
+ ao_cmd_register(&ao_adc_cmds[0]);
+
+ ao_adc_ready = 1;
+}
--- /dev/null
+/*
+ * Copyright © 2023 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_ARCH_H_
+#define _AO_ARCH_H_
+
+#include <stm32f1.h>
+
+#ifndef AO_STACK_SIZE
+#define AO_STACK_SIZE 512
+#endif
+
+#define AO_PORT_TYPE uint16_t
+
+#define ao_arch_naked_declare __attribute__((naked))
+#define ao_arch_naked_define
+#define __interrupt(n)
+#define __at(n)
+
+#define ao_arch_nop() asm("nop")
+
+#define ao_arch_interrupt(n) /* nothing */
+
+#define AO_ROMCONFIG_SYMBOL __attribute__((section(".init.1"))) const
+#define AO_USBCONFIG_SYMBOL __attribute__((section(".init.2"))) const
+
+#define AO_SYSTICK (AO_HCLK / 8)
+
+#if AO_NONMASK_INTERRUPT
+#define AO_STM_NVIC_NONMASK_PRIORITY 0x00
+
+/* Set the basepri register to this value to mask all
+ * non-maskable priorities
+ */
+#define AO_STM_NVIC_BASEPRI_MASK 0x10
+#endif
+
+#define AO_STM_NVIC_HIGH_PRIORITY 0x40
+#define AO_STM_NVIC_MED_PRIORITY 0x80
+#define AO_STM_NVIC_LOW_PRIORITY 0xC0
+#define AO_STM_NVIC_CLOCK_PRIORITY 0xf0
+
+#define AO_PCLK1 AO_APB1CLK
+#define AO_PCLK2 AO_APB2CLK
+
+#if AO_RCC_CFGR_PPRE1_DIV == STM_RCC_CFGR_PPRE1_DIV_1
+#define AO_TIM23467_CLK AO_APB1CLK
+#else
+#define AO_TIM23467_CLK (2 * AO_APB1CLK)
+#endif
+
+/* ADC maximum reported value */
+#define AO_ADC_MAX 4095
+
+#define AO_BOOT_APPLICATION_BASE ((uint32_t *) 0x08001000)
+#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_ */
--- /dev/null
+/*
+ * Copyright © 2023 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_ARCH_FUNCS_H_
+#define _AO_ARCH_FUNCS_H_
+
+#define AO_EXTI_MODE_RISING 1
+#define AO_EXTI_MODE_FALLING 2
+#define AO_EXTI_MODE_PULL_NONE 0
+#define AO_EXTI_MODE_PULL_UP 4
+#define AO_EXTI_MODE_PULL_DOWN 8
+#define AO_EXTI_PRIORITY_LOW 16
+#define AO_EXTI_PRIORITY_MED 0
+#define AO_EXTI_PRIORITY_HIGH 32
+#define AO_EXTI_PIN_NOCONFIGURE 64
+
+static inline void
+ao_enable_port(struct stm_gpio *port)
+{
+ if ((port) == &stm_gpioa)
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_IOPAEN);
+ else if ((port) == &stm_gpiob)
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_IOPBEN);
+ else if ((port) == &stm_gpioc)
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_IOPCEN);
+ else if ((port) == &stm_gpiod)
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_IOPDEN);
+ else if ((port) == &stm_gpioe)
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_IOPEEN);
+}
+
+static inline void
+ao_disable_port(struct stm_gpio *port)
+{
+ if ((port) == &stm_gpioa)
+ stm_rcc.apb2enr &= ~(1UL << STM_RCC_APB2ENR_IOPAEN);
+ else if ((port) == &stm_gpiob)
+ stm_rcc.apb2enr &= ~(1UL << STM_RCC_APB2ENR_IOPBEN);
+ else if ((port) == &stm_gpioc)
+ stm_rcc.apb2enr &= ~(1UL << STM_RCC_APB2ENR_IOPCEN);
+ else if ((port) == &stm_gpiod)
+ stm_rcc.apb2enr &= ~(1UL << STM_RCC_APB2ENR_IOPDEN);
+ else if ((port) == &stm_gpioe)
+ stm_rcc.apb2enr &= ~(1UL << STM_RCC_APB2ENR_IOPEEN);
+}
+
+#define ao_gpio_set(port, bit, v) stm_gpio_set(port, bit, v)
+
+#define ao_gpio_get(port, bit) stm_gpio_get(port, bit)
+
+#define ao_gpio_set_bits(port, bits) stm_gpio_set_bits(port, bits)
+
+#define ao_gpio_set_mask(port, bits, mask) stm_gpio_set_mask(port, bits, mask)
+
+#define ao_gpio_clr_bits(port, bits) stm_gpio_clr_bits(port, bits);
+
+#define ao_gpio_get_all(port) stm_gpio_get_all(port)
+
+static inline void
+ao_enable_output(struct stm_gpio *port, int bit, uint8_t v)
+{
+ ao_enable_port(port);
+ ao_gpio_set(port, bit, v);
+ stm_gpio_conf(port, bit,
+ STM_GPIO_CR_MODE_OUTPUT_10MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_PUSH_PULL);
+}
+
+static inline void
+ao_gpio_set_mode(struct stm_gpio *port, int bit, int mode)
+{
+ uint8_t cnf;
+
+ if (mode == AO_EXTI_MODE_PULL_NONE)
+ cnf = STM_GPIO_CR_CNF_INPUT_FLOATING;
+ else {
+ cnf = STM_GPIO_CR_CNF_INPUT_PULL;
+ ao_gpio_set(port, bit, mode == AO_EXTI_MODE_PULL_UP);
+ }
+ stm_gpio_conf(port, bit,
+ STM_GPIO_CR_MODE_INPUT,
+ cnf);
+}
+
+static inline void
+ao_enable_input(struct stm_gpio *port, int bit, int mode)
+{
+ ao_enable_port(port);
+ ao_gpio_set_mode(port, bit, mode);
+}
+
+static inline void
+ao_enable_cs(struct stm_gpio *port, int bit)
+{
+ ao_enable_output(port, bit, 1);
+}
+
+#if USE_SERIAL_1_SW_FLOW || USE_SERIAL_2_SW_FLOW || USE_SERIAL_3_SW_FLOW
+#define HAS_SERIAL_SW_FLOW 1
+#else
+#define HAS_SERIAL_SW_FLOW 0
+#endif
+
+#if USE_SERIAL_1_FLOW && !USE_SERIAL_1_SW_FLOW || USE_SERIAL_2_FLOW && !USE_SERIAL_2_SW_FLOW || USE_SERIAL_3_FLOW && !USE_SERIAL_3_SW_FLOW
+#define HAS_SERIAL_HW_FLOW 1
+#else
+#define HAS_SERIAL_HW_FLOW 0
+#endif
+
+/* ao_serial_stm.c */
+struct ao_stm_usart {
+ struct ao_fifo rx_fifo;
+ struct ao_fifo tx_fifo;
+ struct stm_usart *reg;
+ uint8_t tx_running;
+ uint8_t draining;
+#if HAS_SERIAL_SW_FLOW
+ /* RTS - 0 if we have FIFO space, 1 if not
+ * CTS - 0 if we can send, 0 if not
+ */
+ struct stm_gpio *gpio_rts;
+ struct stm_gpio *gpio_cts;
+ uint8_t pin_rts;
+ uint8_t pin_cts;
+ uint8_t rts;
+#endif
+};
+
+void
+ao_debug_out(char c);
+
+#if HAS_SERIAL_1
+extern struct ao_stm_usart ao_stm_usart1;
+#endif
+
+#if HAS_SERIAL_2
+extern struct ao_stm_usart ao_stm_usart2;
+#endif
+
+#if HAS_SERIAL_3
+extern struct ao_stm_usart ao_stm_usart3;
+#endif
+
+#define ARM_PUSH32(stack, val) (*(--(stack)) = (val))
+
+typedef uint32_t ao_arch_irq_t;
+
+static inline void
+ao_arch_block_interrupts(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+ asm("msr basepri,%0" : : "r" (AO_STM_NVIC_BASEPRI_MASK));
+#else
+ asm("cpsid i");
+#endif
+}
+
+static inline void
+ao_arch_release_interrupts(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+ asm("msr basepri,%0" : : "r" (0x0));
+#else
+ asm("cpsie i");
+#endif
+}
+
+static inline uint32_t
+ao_arch_irqsave(void) {
+ uint32_t val;
+#ifdef AO_NONMASK_INTERRUPTS
+ asm("mrs %0,basepri" : "=r" (val));
+#else
+ asm("mrs %0,primask" : "=r" (val));
+#endif
+ ao_arch_block_interrupts();
+ return val;
+}
+
+static inline void
+ao_arch_irqrestore(uint32_t basepri) {
+#ifdef AO_NONMASK_INTERRUPTS
+ asm("msr basepri,%0" : : "r" (basepri));
+#else
+ asm("msr primask,%0" : : "r" (basepri));
+#endif
+}
+
+static inline void
+ao_arch_memory_barrier(void) {
+ asm volatile("" ::: "memory");
+}
+
+static inline void
+ao_arch_irq_check(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+ uint32_t basepri;
+ asm("mrs %0,basepri" : "=r" (basepri));
+ if (basepri == 0)
+ ao_panic(AO_PANIC_IRQ);
+#else
+ uint32_t primask;
+ asm("mrs %0,primask" : "=r" (primask));
+ if ((primask & 1) == 0)
+ ao_panic(AO_PANIC_IRQ);
+#endif
+}
+
+#if HAS_TASK
+static inline void
+ao_arch_init_stack(struct ao_task *task, uint32_t *sp, void *start)
+{
+ uint32_t a = (uint32_t) start;
+ int i;
+
+ /* Return address (goes into LR) */
+ ARM_PUSH32(sp, a);
+
+ /* Clear register values r0-r12 */
+ i = 13;
+ while (i--)
+ ARM_PUSH32(sp, 0);
+
+ /* APSR */
+ ARM_PUSH32(sp, 0);
+
+ /* BASEPRI with interrupts enabled */
+ ARM_PUSH32(sp, 0);
+
+ task->sp32 = sp;
+}
+
+static inline void ao_arch_save_regs(void) {
+ /* Save general registers */
+ asm("push {r0-r12,lr}\n");
+
+ /* Save APSR */
+ asm("mrs r0,apsr");
+ asm("push {r0}");
+
+#ifdef AO_NONMASK_INTERRUPTS
+ /* Save BASEPRI */
+ asm("mrs r0,basepri");
+#else
+ /* Save PRIMASK */
+ asm("mrs r0,primask");
+#endif
+ asm("push {r0}");
+}
+
+static inline void ao_arch_save_stack(void) {
+ uint32_t *sp;
+ asm("mov %0,sp" : "=&r" (sp) );
+ ao_cur_task->sp32 = (sp);
+}
+
+static inline void ao_arch_restore_stack(void) {
+ /* Switch stacks */
+ asm("mov sp, %0" : : "r" (ao_cur_task->sp32) );
+
+#ifdef AO_NONMASK_INTERRUPTS
+ /* Restore BASEPRI */
+ asm("pop {r0}");
+ asm("msr basepri,r0");
+#else
+ /* Restore PRIMASK */
+ asm("pop {r0}");
+ asm("msr primask,r0");
+#endif
+
+ /* Restore APSR */
+ asm("pop {r0}");
+ asm("msr apsr_nczvq,r0");
+
+ /* Restore general registers */
+ asm("pop {r0-r12,lr}\n");
+
+ /* Return to calling function */
+ asm("bx lr");
+}
+#define HAS_ARCH_START_SCHEDULER 1
+
+static inline void ao_arch_start_scheduler(void) {
+ uint32_t sp;
+ uint32_t control;
+
+ asm("mrs %0,msp" : "=&r" (sp));
+ asm("msr psp,%0" : : "r" (sp));
+ asm("mrs %0,control" : "=r" (control));
+ control |= (1 << 1);
+ asm("msr control,%0" : : "r" (control));
+ asm("isb");
+}
+
+#define ao_arch_isr_stack()
+
+#endif /* HAS_TASK */
+
+static inline void
+ao_arch_wait_interrupt(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+ asm(
+ "dsb\n" /* Serialize data */
+ "isb\n" /* Serialize instructions */
+ "cpsid i\n" /* Block all interrupts */
+ "msr basepri,%0\n" /* Allow all interrupts through basepri */
+ "wfi\n" /* Wait for an interrupt */
+ "cpsie i\n" /* Allow all interrupts */
+ "msr basepri,%1\n" /* Block interrupts through basepri */
+ : : "r" (0), "r" (AO_STM_NVIC_BASEPRI_MASK));
+#else
+ asm("\twfi\n");
+ ao_arch_release_interrupts();
+ ao_arch_block_interrupts();
+#endif
+}
+
+#define ao_arch_critical(b) do { \
+ uint32_t __mask = ao_arch_irqsave(); \
+ do { b } while (0); \
+ ao_arch_irqrestore(__mask); \
+ } while (0)
+
+#define ao_arch_reboot() \
+ (stm_scb.aircr = ((STM_SCB_AIRCR_VECTKEY_KEY << STM_SCB_AIRCR_VECTKEY) | \
+ (1 << STM_SCB_AIRCR_SYSRESETREQ)))
+
+/* ao_dma_stm.c
+ */
+
+extern uint8_t ao_dma_done[STM_NUM_DMA];
+
+void
+ao_dma_set_transfer(uint8_t index,
+ volatile void *peripheral,
+ void *memory,
+ uint16_t count,
+ uint32_t ccr);
+
+void
+ao_dma_mutex_get(uint8_t index);
+
+void
+ao_dma_mutex_put(uint8_t index);
+
+void
+ao_dma_set_isr(uint8_t index, void (*isr)(int index));
+
+void
+ao_dma_start(uint8_t index);
+
+void
+ao_dma_done_transfer(uint8_t index);
+
+void
+ao_dma_alloc(uint8_t index);
+
+void
+ao_dma_init(void);
+
+/* ao_spi_stm.c
+ */
+
+#define AO_SPI_CPOL_BIT 4
+#define AO_SPI_CPHA_BIT 5
+
+#define AO_SPI_CONFIG_1 0x00
+#define AO_SPI_1_CONFIG_PA5_PA6_PA7 AO_SPI_CONFIG_1
+#define AO_SPI_2_CONFIG_PB13_PB14_PB15 AO_SPI_CONFIG_1
+
+#define AO_SPI_CONFIG_2 0x04
+#define AO_SPI_1_CONFIG_PB3_PB4_PB5 AO_SPI_CONFIG_2
+#define AO_SPI_2_CONFIG_PD1_PD3_PD4 AO_SPI_CONFIG_2
+
+#define AO_SPI_CONFIG_3 0x08
+#define AO_SPI_1_CONFIG_PE13_PE14_PE15 AO_SPI_CONFIG_3
+
+#define AO_SPI_CONFIG_NONE 0x0c
+
+#define AO_SPI_INDEX_MASK 0x01
+#define AO_SPI_CONFIG_MASK 0x0c
+
+#define AO_SPI_1_PA5_PA6_PA7 (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PA5_PA6_PA7)
+#define AO_SPI_1_PB3_PB4_PB5 (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PB3_PB4_PB5)
+#define AO_SPI_1_PE13_PE14_PE15 (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PE13_PE14_PE15)
+
+#define AO_SPI_2_PB13_PB14_PB15 (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PB13_PB14_PB15)
+#define AO_SPI_2_PD1_PD3_PD4 (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PD1_PD3_PD4)
+
+#define AO_SPI_INDEX(id) ((id) & AO_SPI_INDEX_MASK)
+#define AO_SPI_CONFIG(id) ((id) & AO_SPI_CONFIG_MASK)
+#define AO_SPI_PIN_CONFIG(id) ((id) & (AO_SPI_INDEX_MASK | AO_SPI_CONFIG_MASK))
+#define AO_SPI_CPOL(id) ((uint32_t) (((id) >> AO_SPI_CPOL_BIT) & 1))
+#define AO_SPI_CPHA(id) ((uint32_t) (((id) >> AO_SPI_CPHA_BIT) & 1))
+
+#define AO_SPI_MAKE_MODE(pol,pha) (((pol) << AO_SPI_CPOL_BIT) | ((pha) << AO_SPI_CPHA_BIT))
+#define AO_SPI_MODE_0 AO_SPI_MAKE_MODE(0,0)
+#define AO_SPI_MODE_1 AO_SPI_MAKE_MODE(0,1)
+#define AO_SPI_MODE_2 AO_SPI_MAKE_MODE(1,0)
+#define AO_SPI_MODE_3 AO_SPI_MAKE_MODE(1,1)
+
+/* SPI1 is on APB2, SPI2 is on APB1 */
+#define AO_SPI_FREQ(bus, div) ((AO_SPI_INDEX(bus) == 0 ? AO_APB2CLK : AO_APB1CLK) / (div))
+
+static inline uint32_t
+ao_spi_speed(int num, uint32_t hz)
+{
+ if (hz >= AO_SPI_FREQ(num, 2)) return STM_SPI_CR1_BR_PCLK_2;
+ if (hz >= AO_SPI_FREQ(num, 4)) return STM_SPI_CR1_BR_PCLK_4;
+ if (hz >= AO_SPI_FREQ(num, 8)) return STM_SPI_CR1_BR_PCLK_8;
+ if (hz >= AO_SPI_FREQ(num, 16)) return STM_SPI_CR1_BR_PCLK_16;
+ if (hz >= AO_SPI_FREQ(num, 32)) return STM_SPI_CR1_BR_PCLK_32;
+ if (hz >= AO_SPI_FREQ(num, 64)) return STM_SPI_CR1_BR_PCLK_64;
+ if (hz >= AO_SPI_FREQ(num, 128)) return STM_SPI_CR1_BR_PCLK_128;
+ return STM_SPI_CR1_BR_PCLK_256;
+}
+
+uint8_t
+ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id);
+
+void
+ao_spi_get(uint8_t spi_index, uint32_t speed);
+
+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);
+
+void
+ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_send_sync(const void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_start_bytes(uint8_t spi_index);
+
+void
+ao_spi_stop_bytes(uint8_t spi_index);
+
+static inline void
+ao_spi_send_byte(uint8_t byte, uint8_t spi_index)
+{
+ struct stm_spi *stm_spi;
+
+ switch (AO_SPI_INDEX(spi_index)) {
+ case 0:
+ stm_spi = &stm_spi1;
+ break;
+ case 1:
+ stm_spi = &stm_spi2;
+ break;
+ }
+
+ while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE)))
+ ;
+ stm_spi->dr = byte;
+ while (!(stm_spi->sr & (1 << STM_SPI_SR_RXNE)))
+ ;
+ (void) stm_spi->dr;
+}
+
+static inline uint8_t
+ao_spi_recv_byte(uint8_t spi_index)
+{
+ struct stm_spi *stm_spi;
+
+ switch (AO_SPI_INDEX(spi_index)) {
+ case 0:
+ stm_spi = &stm_spi1;
+ break;
+ case 1:
+ stm_spi = &stm_spi2;
+ break;
+ }
+
+ while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE)))
+ ;
+ stm_spi->dr = 0xff;
+ while (!(stm_spi->sr & (1 << STM_SPI_SR_RXNE)))
+ ;
+ return (uint8_t) stm_spi->dr;
+}
+
+void
+ao_spi_recv(void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_init(void);
+
+#define ao_spi_init_cs(port, mask) do { \
+ uint8_t __bit__; \
+ for (__bit__ = 0; __bit__ < 32; __bit__++) { \
+ if (mask & (1 << __bit__)) \
+ ao_enable_output(port, __bit__, 1); \
+ } \
+ } while (0)
+
+#define ao_spi_set_cs(reg,mask) ((reg)->bsrr = ((uint32_t) (mask)) << 16)
+#define ao_spi_clr_cs(reg,mask) ((reg)->bsrr = (mask))
+
+#define ao_spi_get_mask(reg,mask,bus, speed) do { \
+ ao_spi_get(bus, speed); \
+ ao_spi_set_cs(reg,mask); \
+ } while (0)
+
+static inline uint8_t
+ao_spi_try_get_mask(struct stm_gpio *reg, uint16_t mask, uint8_t bus, uint32_t speed, uint8_t task_id)
+{
+ if (!ao_spi_try_get(bus, speed, task_id))
+ return 0;
+ ao_spi_set_cs(reg, mask);
+ return 1;
+}
+
+#define ao_spi_put_mask(reg,mask,bus) do { \
+ ao_spi_clr_cs(reg,mask); \
+ ao_spi_put(bus); \
+ } while (0)
+
+#define ao_spi_get_bit(reg,bit,bus,speed) ao_spi_get_mask(reg,1<<(bit),bus,speed)
+#define ao_spi_put_bit(reg,bit,bus) ao_spi_put_mask(reg,1<<(bit),bus)
+
+void
+ao_i2c_get(uint8_t i2c_index);
+
+uint8_t
+ao_i2c_start(uint8_t i2c_index, uint16_t address);
+
+void
+ao_i2c_put(uint8_t i2c_index);
+
+uint8_t
+ao_i2c_send(void *block, uint16_t len, uint8_t i2c_index, uint8_t stop);
+
+uint8_t
+ao_i2c_recv(void *block, uint16_t len, uint8_t i2c_index, uint8_t stop);
+
+void
+ao_i2c_init(void);
+
+#endif /* _AO_ARCH_FUNCS_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; 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_beep.h"
+
+#if BEEPER_TIMER == 2
+#define stm_beeper stm_tim2
+#define RCC_BEEPER STM_RCC_APB1ENR_TIM2EN
+#elif BEEPER_TIMER == 3
+#define stm_beeper stm_tim3
+#define RCC_BEEPER STM_RCC_APB1ENR_TIM3EN
+#elif BEEPER_TIMER == 4
+#define stm_beeper stm_tim4
+#define RCC_BEEPER STM_RCC_APB1ENR_TIM4EN
+#else
+#error BEEPER_TIMER must be 2, 3 or 4
+#endif
+
+void
+ao_beep(uint8_t beep)
+{
+ if (beep == 0) {
+ stm_beeper.cr1 = 0;
+ stm_rcc.apb1enr &= ~(1UL << RCC_BEEPER);
+ stm_gpio_conf(BEEPER_PORT, BEEPER_PIN,
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_PUSH_PULL);
+ } else {
+ stm_gpio_conf(BEEPER_PORT, BEEPER_PIN,
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+ stm_rcc.apb1enr |= (1UL << RCC_BEEPER);
+
+ stm_beeper.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
+ (STM_TIM234_CR2_MMS_RESET << STM_TIM234_CR2_MMS) |
+ (0 << STM_TIM234_CR2_CCDS));
+
+ /* Set prescaler to match cc1111 clocks
+ */
+ stm_beeper.psc = AO_TIM23467_CLK / 750000;
+
+ /* 1. Select the counter clock (internal, external, prescaler).
+ *
+ * Setting SMCR to zero means use the internal clock
+ */
+
+ stm_beeper.smcr = 0;
+
+ /* 2. Write the desired data in the TIMx_ARR and TIMx_CCRx registers. */
+ stm_beeper.arr = beep;
+#if BEEPER_CHANNEL == 1
+ stm_beeper.ccr1 = beep;
+#elif BEEPER_CHANNEL == 2
+ stm_beeper.ccr2 = beep;
+#elif BEEPER_CHANNEL == 3
+ stm_beeper.ccr3 = beep;
+#elif BEEPER_CHANNEL == 4
+ stm_beeper.ccr4 = beep;
+#else
+#error invalid BEEPER_CHANNEL
+#endif
+
+ /* 3. Set the CCxIE and/or CCxDE bits if an interrupt and/or a
+ * DMA request is to be generated.
+ */
+ /* don't want this */
+
+ /* 4. Select the output mode. For example, you must write
+ * OCxM=011, OCxPE=0, CCxP=0 and CCxE=1 to toggle OCx output
+ * pin when CNT matches CCRx, CCRx preload is not used, OCx
+ * is enabled and active high.
+ */
+
+#define OC1M (BEEPER_CHANNEL == 1 ? STM_TIM234_CCMR1_OC1M_TOGGLE : STM_TIM234_CCMR1_OC1M_FROZEN)
+#define OC2M (BEEPER_CHANNEL == 2 ? STM_TIM234_CCMR1_OC2M_TOGGLE : STM_TIM234_CCMR1_OC2M_FROZEN)
+#define OC3M (BEEPER_CHANNEL == 3 ? STM_TIM234_CCMR2_OC3M_TOGGLE : STM_TIM234_CCMR2_OC3M_FROZEN)
+#define OC4M (BEEPER_CHANNEL == 4 ? STM_TIM234_CCMR2_OC4M_TOGGLE : STM_TIM234_CCMR2_OC4M_FROZEN)
+
+#define CCER(n) (BEEPER_CHANNEL == (n) ? 1 : 0)
+
+#if BEEPER_CHANNEL == 1 || BEEPER_CHANNEL == 2
+ stm_beeper.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
+ (OC2M << STM_TIM234_CCMR1_OC2M) |
+ (0 << STM_TIM234_CCMR1_OC2PE) |
+ (0 << STM_TIM234_CCMR1_OC2FE) |
+ (STM_TIM234_CCMR1_CC2S_OUTPUT << STM_TIM234_CCMR1_CC2S) |
+
+ (0 << STM_TIM234_CCMR1_OC1CE) |
+ (OC1M << STM_TIM234_CCMR1_OC1M) |
+ (0 << STM_TIM234_CCMR1_OC1PE) |
+ (0 << STM_TIM234_CCMR1_OC1FE) |
+ (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
+#elif BEEPER_CHANNEL == 3 || BEEPER_CHANNEL == 4
+ stm_beeper.ccmr2 = ((0 << STM_TIM234_CCMR2_OC4CE) |
+ (OC4M << 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) |
+ (OC3M << STM_TIM234_CCMR2_OC3M) |
+ (0 << STM_TIM234_CCMR2_OC3PE) |
+ (0 << STM_TIM234_CCMR2_OC3FE) |
+ (STM_TIM234_CCMR2_CC3S_OUTPUT << STM_TIM234_CCMR2_CC3S));
+#else
+#error invalid BEEPER_CHANNEL
+#endif
+ stm_beeper.ccer = ((0 << STM_TIM234_CCER_CC4NP) |
+ (0 << STM_TIM234_CCER_CC4P) |
+ (CCER(4) << STM_TIM234_CCER_CC4E) |
+ (0 << STM_TIM234_CCER_CC3NP) |
+ (0 << STM_TIM234_CCER_CC3P) |
+ (CCER(3) << STM_TIM234_CCER_CC3E) |
+ (0 << STM_TIM234_CCER_CC2NP) |
+ (0 << STM_TIM234_CCER_CC2P) |
+ (CCER(2) << STM_TIM234_CCER_CC2E) |
+ (0 << STM_TIM234_CCER_CC1NP) |
+ (0 << STM_TIM234_CCER_CC1P) |
+ (CCER(1) << STM_TIM234_CCER_CC1E));
+
+ /* 5. Enable the counter by setting the CEN bit in the TIMx_CR1 register. */
+
+ stm_beeper.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+ (0 << STM_TIM234_CR1_ARPE) |
+ (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+ (0 << STM_TIM234_CR1_DIR) |
+ (0 << STM_TIM234_CR1_OPM) |
+ (0 << STM_TIM234_CR1_URS) |
+ (0 << STM_TIM234_CR1_UDIS) |
+ (1 << STM_TIM234_CR1_CEN));
+
+ /* Update the values */
+ stm_beeper.egr = (1 << STM_TIM234_EGR_UG);
+ }
+}
+
+void
+ao_beep_for(uint8_t beep, AO_TICK_TYPE ticks)
+{
+ ao_beep(beep);
+ ao_delay(ticks);
+ ao_beep(0);
+}
+
+void
+ao_beep_init(void)
+{
+#if BEEPER_TIMER == 2
+ if (BEEPER_PORT == &stm_gpioa) {
+ switch (BEEPER_PIN) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ stm_set_afio_mapr(STM_AFIO_MAPR_TIM2_REMAP,
+ STM_AFIO_MAPR_TIM2_REMAP_PA0_PA1_PA2_PA3,
+ STM_AFIO_MAPR_TIM2_REMAP_MASK);
+ break;
+ default:
+ ao_panic(AO_PANIC_CRASH);
+ break;
+ }
+ }
+#elif BEEPER_TIMER == 3
+ if (BEEPER_PORT == &stm_gpioa) {
+ switch (BEEPER_PIN) {
+ case 6:
+ case 7:
+ stm_set_afio_mapr(STM_AFIO_MAPR_TIM3_REMAP,
+ STM_AFIO_MAPR_TIM3_REMAP_PA6_PA7_PB0_PB1,
+ STM_AFIO_MAPR_TIM3_REMAP_MASK);
+ break;
+ default:
+ ao_panic(AO_PANIC_CRASH);
+ break;
+ }
+ } else if (BEEPER_PORT == &stm_gpiob) {
+ switch (BEEPER_PIN) {
+ case 4:
+ case 5:
+ case 0:
+ case 1:
+ stm_set_afio_mapr(STM_AFIO_MAPR_TIM3_REMAP,
+ STM_AFIO_MAPR_TIM3_REMAP_PB4_PB5_PB0_PB1,
+ STM_AFIO_MAPR_TIM3_REMAP_MASK);
+ break;
+ default:
+ ao_panic(AO_PANIC_CRASH);
+ break;
+ }
+ } else if (BEEPER_PORT == &stm_gpioc) {
+ switch (BEEPER_PIN) {
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ stm_set_afio_mapr(STM_AFIO_MAPR_TIM3_REMAP,
+ STM_AFIO_MAPR_TIM3_REMAP_PC6_PC7_PC8_PC9,
+ STM_AFIO_MAPR_TIM3_REMAP_MASK);
+ break;
+ default:
+ ao_panic(AO_PANIC_CRASH);
+ break;
+ }
+ }
+#elif BEEPER_TIMER == 4
+ ao_panic(AO_PANIC_CRASH);
+#endif
+ ao_enable_output(BEEPER_PORT, BEEPER_PIN, 0);
+
+ /* Leave the timer off until requested */
+ stm_rcc.apb1enr &= ~(1UL << RCC_BEEPER);
+}
--- /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; 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_boot.h>
+
+void
+ao_boot_chain(uint32_t *base)
+{
+ uint32_t sp;
+ uint32_t pc;
+
+ sp = base[0];
+ pc = base[1];
+ if (0x08000100 <= pc && pc <= 0x08200000 && (pc & 1) == 1) {
+ asm ("mov sp, %0" : : "r" (sp));
+ asm ("mov lr, %0" : : "r" (pc));
+ asm ("bx lr");
+ }
+}
+
+#define AO_BOOT_SIGNAL 0x5a5aa5a5
+#define AO_BOOT_CHECK 0xc3c33c3c
+
+struct ao_boot {
+ uint32_t *base;
+ uint32_t signal;
+ uint32_t check;
+};
+
+struct ao_boot ao_boot __attribute__((section(".preserve.2")));
+
+int
+ao_boot_check_chain(void)
+{
+ if (ao_boot.signal == AO_BOOT_SIGNAL && ao_boot.check == AO_BOOT_CHECK) {
+ ao_boot.signal = 0;
+ ao_boot.check = 0;
+ if (ao_boot.base == AO_BOOT_FORCE_LOADER)
+ return 0;
+ ao_boot_chain(ao_boot.base);
+ }
+ return 1;
+}
+
+void
+ao_boot_reboot(uint32_t *base)
+{
+ ao_boot.base = base;
+ ao_boot.signal = AO_BOOT_SIGNAL;
+ ao_boot.check = AO_BOOT_CHECK;
+ ao_arch_reboot();
+}
--- /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; 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_boot.h>
+#include <ao_exti.h>
+
+int
+ao_boot_check_pin(void)
+{
+ uint16_t v;
+
+ /* 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);
+
+ for (v = 0; v < 100; v++)
+ ao_arch_nop();
+
+ /* Read the value */
+ v = ao_gpio_get(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN);
+
+ /* Reset the chip to turn off the port and the power interface clock */
+ ao_gpio_set_mode(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN, 0);
+ ao_disable_port(&AO_BOOT_APPLICATION_GPIO);
+
+ stm_rcc.apb1enr &= ~(1UL << STM_RCC_APB1ENR_PWREN);
+ return v == AO_BOOT_APPLICATION_VALUE;
+}
--- /dev/null
+/*
+ * Copyright © 2023 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"
+
+void
+ao_clock_init(void)
+{
+ uint32_t cfgr;
+
+ /* Switch to HSI while messing about */
+ stm_rcc.cr |= (1 << STM_RCC_CR_HSION);
+ while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
+ ao_arch_nop();
+
+ stm_rcc.cfgr = (stm_rcc.cfgr & ~(uint32_t) (STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW)) |
+ (STM_RCC_CFGR_SW_HSI << STM_RCC_CFGR_SW);
+
+ /* wait for system to switch to HSI */
+ while ((stm_rcc.cfgr & (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS)) !=
+ (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS))
+ ao_arch_nop();
+
+ /* Disable all interrupts */
+ stm_rcc.cir = 0;
+
+#if AO_HSE
+#if AO_HSE_BYPASS
+ stm_rcc.cr |= (1 << STM_RCC_CR_HSEBYP);
+#else
+ stm_rcc.cr &= ~(uint32_t) (1 << STM_RCC_CR_HSEBYP);
+#endif
+ /* Enable HSE clock */
+ stm_rcc.cr |= (1 << STM_RCC_CR_HSEON);
+ while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSERDY)))
+ asm("nop");
+
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK (STM_RCC_CFGR_SWS_HSE << STM_RCC_CFGR_SWS)
+#define STM_RCC_CFGR_SW_TARGET_CLOCK (STM_RCC_CFGR_SW_HSE)
+#define STM_PLLSRC AO_HSE
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK (STM_RCC_CFGR_PLLSRC_HSE << STM_RCC_CFGR_PLLSRC)
+#else
+#define STM_HSI 16000000
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS)
+#define STM_RCC_CFGR_SW_TARGET_CLOCK (STM_RCC_CFGR_SW_HSI)
+#define STM_PLLSRC (STM_HSI/2)
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK (STM_RCC_CFGR_PLLSRC_HSI_2 << STM_RCC_CFGR_PLLSRC)
+#endif
+
+#if !AO_HSE || HAS_ADC || HAS_ADC_SINGLE
+ /* Enable HSI RC clock 16MHz */
+ stm_rcc.cr |= (1 << STM_RCC_CR_HSION);
+ while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
+ asm("nop");
+#endif
+
+ /* Set flash latency to tolerate 72MHz SYSCLK -> 2 wait states */
+
+ /* Enable 64-bit access and prefetch */
+ stm_flash.acr = ((1 << STM_FLASH_ACR_PRFTBE) |
+ (0 << STM_FLASH_ACR_HLFCYA) |
+ (STM_FLASH_ACR_LATENCY_2 << STM_FLASH_ACR_LATENCY));
+
+ /* Enable power interface clock */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
+
+#if 0
+ /* Set voltage range to 1.8V */
+
+ /* poll VOSF bit in PWR_CSR. Wait until it is reset to 0 */
+ while ((stm_pwr.csr & (1 << STM_PWR_CSR_VOSF)) != 0)
+ asm("nop");
+
+ /* Configure voltage scaling range */
+ cr = stm_pwr.cr;
+ cr &= ~(STM_PWR_CR_VOS_MASK << STM_PWR_CR_VOS);
+ cr |= (STM_PWR_CR_VOS_1_8 << STM_PWR_CR_VOS);
+ stm_pwr.cr = cr;
+
+ /* poll VOSF bit in PWR_CSR. Wait until it is reset to 0 */
+ while ((stm_pwr.csr & (1 << STM_PWR_CSR_VOSF)) != 0)
+ asm("nop");
+#endif
+
+ /* HCLK */
+ cfgr = stm_rcc.cfgr;
+ cfgr &= ~(STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE);
+ cfgr |= (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE);
+ stm_rcc.cfgr = cfgr;
+ while ((stm_rcc.cfgr & (STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE)) !=
+ (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE))
+ asm ("nop");
+
+ /* APB1 Prescaler = AO_APB1_PRESCALER */
+ cfgr = stm_rcc.cfgr;
+ cfgr &= ~(STM_RCC_CFGR_PPRE1_MASK << STM_RCC_CFGR_PPRE1);
+ cfgr |= (AO_RCC_CFGR_PPRE1_DIV << STM_RCC_CFGR_PPRE1);
+ stm_rcc.cfgr = cfgr;
+
+ /* APB2 Prescaler = AO_APB2_PRESCALER */
+ cfgr = stm_rcc.cfgr;
+ cfgr &= ~(STM_RCC_CFGR_PPRE2_MASK << STM_RCC_CFGR_PPRE2);
+ cfgr |= (AO_RCC_CFGR_PPRE2_DIV << STM_RCC_CFGR_PPRE2);
+ stm_rcc.cfgr = cfgr;
+
+ /* ADC Prescaler */
+ cfgr = stm_rcc.cfgr;
+ cfgr &= ~(STM_RCC_CFGR_ADCPRE_MASK << STM_RCC_CFGR_ADCPRE);
+ cfgr |= (AO_RCC_CFGR_ADCPRE << STM_RCC_CFGR_ADCPRE);
+ stm_rcc.cfgr = cfgr;
+
+ /* Disable the PLL */
+ stm_rcc.cr &= ~(1UL << STM_RCC_CR_PLLON);
+ while (stm_rcc.cr & (1UL << STM_RCC_CR_PLLRDY))
+ asm("nop");
+
+ /* PLLMUL */
+ cfgr = stm_rcc.cfgr;
+ cfgr &= ~(STM_RCC_CFGR_PLLMUL_MASK << STM_RCC_CFGR_PLLMUL);
+ cfgr |= (AO_RCC_CFGR_PLLMUL << STM_RCC_CFGR_PLLMUL);
+
+ /* PLLXTPRE */
+ cfgr &= ~(STM_RCC_CFGR_PLLXTPRE_MASK << STM_RCC_CFGR_PLLXTPRE);
+ cfgr |= (AO_RCC_CFGR_PLLXTPRE << STM_RCC_CFGR_PLLXTPRE);
+
+ /* PLL source */
+ cfgr &= ~(1UL << STM_RCC_CFGR_PLLSRC);
+ cfgr |= STM_RCC_CFGR_PLLSRC_TARGET_CLOCK;
+
+ stm_rcc.cfgr = cfgr;
+
+ /* Enable the PLL and wait for it */
+ stm_rcc.cr |= (1 << STM_RCC_CR_PLLON);
+ while (!(stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY)))
+ asm("nop");
+
+ /* Switch to the PLL for the system clock */
+
+ cfgr = stm_rcc.cfgr;
+ cfgr &= ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW);
+ cfgr |= (STM_RCC_CFGR_SW_PLL << STM_RCC_CFGR_SW);
+ stm_rcc.cfgr = cfgr;
+ for (;;) {
+ uint32_t c, part, mask, val;
+
+ c = stm_rcc.cfgr;
+ mask = (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS);
+ val = (STM_RCC_CFGR_SWS_PLL << STM_RCC_CFGR_SWS);
+ part = c & mask;
+ if (part == val)
+ break;
+ }
+
+#if 0
+ stm_rcc.apb2rstr = 0xffff;
+ stm_rcc.apb1rstr = 0xffff;
+ stm_rcc.ahbrstr = 0x3f;
+ stm_rcc.ahbenr = (1 << STM_RCC_AHBENR_FLITFEN);
+ stm_rcc.apb2enr = 0;
+ stm_rcc.apb1enr = 0;
+ stm_rcc.ahbrstr = 0;
+ stm_rcc.apb1rstr = 0;
+ stm_rcc.apb2rstr = 0;
+#endif
+
+ /* Clear reset flags */
+ stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
+
+ /* Enable AFIO */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_AFIOEN);
+
+ /* Release PB3, PA15 and PB4 from JTAG use */
+ stm_afio.mapr = (stm_afio.mapr &
+ ~(STM_AFIO_MAPR_SWJ_CFG_MASK << STM_AFIO_MAPR_SWJ_CFG)) |
+ STM_AFIO_MAPR_SWJ_CFG_SW_DP << STM_AFIO_MAPR_SWJ_CFG;
+
+#if DEBUG_THE_CLOCK
+ /* Output SYSCLK on PA8 for measurments */
+
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+
+ stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0);
+ stm_moder_set(&stm_gpioa, 8, STM_MODER_ALTERNATE);
+ stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_40MHz);
+
+ stm_rcc.cfgr |= (STM_RCC_CFGR_MCOPRE_DIV_1 << STM_RCC_CFGR_MCOPRE);
+ stm_rcc.cfgr |= (STM_RCC_CFGR_MCOSEL_HSE << STM_RCC_CFGR_MCOSEL);
+#endif
+}
--- /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; 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"
+
+#define NUM_DMA 7
+
+struct ao_dma_config {
+ void (*isr)(int index);
+};
+
+uint8_t ao_dma_done[NUM_DMA];
+
+static struct ao_dma_config ao_dma_config[NUM_DMA];
+static uint8_t ao_dma_allocated[NUM_DMA];
+static uint8_t ao_dma_mutex[NUM_DMA];
+
+static void
+ao_dma_isr(uint8_t index) {
+ /* Get channel interrupt bits */
+ uint32_t isr = stm_dma.isr & (STM_DMA_ISR_MASK <<
+ STM_DMA_ISR(index));
+
+ /* Ack them */
+ stm_dma.ifcr = isr;
+ if (ao_dma_config[index].isr)
+ (*ao_dma_config[index].isr)(index);
+ else {
+ ao_dma_done[index] = 1;
+ ao_wakeup(&ao_dma_done[index]);
+ }
+}
+
+void stm_dma1_channel1_isr(void) { ao_dma_isr(STM_DMA_INDEX(1)); }
+void stm_dma1_channel2_isr(void) { ao_dma_isr(STM_DMA_INDEX(2)); }
+#ifdef STM_DMA1_3_STOLEN
+#define LEAVE_DMA_ON
+#else
+void stm_dma1_channel3_isr(void) { ao_dma_isr(STM_DMA_INDEX(3)); }
+#endif
+void stm_dma1_channel4_isr(void) { ao_dma_isr(STM_DMA_INDEX(4)); }
+#ifdef STM_DMA1_5_STOLEN
+#define LEAVE_DMA_ON
+#else
+void stm_dma1_channel5_isr(void) { ao_dma_isr(STM_DMA_INDEX(5)); }
+#endif
+void stm_dma1_channel6_isr(void) { ao_dma_isr(STM_DMA_INDEX(6)); }
+void stm_dma1_channel7_isr(void) { ao_dma_isr(STM_DMA_INDEX(7)); }
+
+#ifndef LEAVE_DMA_ON
+static uint8_t ao_dma_active;
+#endif
+
+void
+ao_dma_mutex_get(uint8_t index)
+{
+ if (ao_dma_allocated[index]) {
+ if (ao_dma_mutex[index])
+ ao_panic(AO_PANIC_DMA);
+ ao_dma_mutex[index] = 0xff;
+ } else
+ ao_mutex_get(&ao_dma_mutex[index]);
+}
+
+void
+ao_dma_mutex_put(uint8_t index)
+{
+ if (ao_dma_allocated[index])
+ ao_dma_mutex[index] = 0;
+ else
+ ao_mutex_put(&ao_dma_mutex[index]);
+}
+
+
+void
+ao_dma_set_transfer(uint8_t index,
+ volatile void *peripheral,
+ void *memory,
+ uint16_t count,
+ uint32_t ccr)
+{
+ if (ao_dma_allocated[index]) {
+ if (ao_dma_mutex[index])
+ ao_panic(AO_PANIC_DMA);
+ ao_dma_mutex[index] = 0xff;
+ } else
+ ao_mutex_get(&ao_dma_mutex[index]);
+#ifndef LEAVE_DMA_ON
+ ao_arch_critical(
+ if (ao_dma_active++ == 0)
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+ );
+#endif
+ stm_dma.channel[index].ccr = ccr | (1 << STM_DMA_CCR_TCIE);
+ stm_dma.channel[index].cndtr = count;
+ stm_dma.channel[index].cpar = peripheral;
+ stm_dma.channel[index].cmar = memory;
+ ao_dma_config[index].isr = NULL;
+}
+
+void
+ao_dma_set_isr(uint8_t index, void (*isr)(int))
+{
+ ao_dma_config[index].isr = isr;
+}
+
+void
+ao_dma_start(uint8_t index)
+{
+ ao_dma_done[index] = 0;
+ stm_dma.channel[index].ccr |= (1UL << STM_DMA_CCR_EN);
+}
+
+void
+ao_dma_done_transfer(uint8_t index)
+{
+ stm_dma.channel[index].ccr &= ~(1UL << STM_DMA_CCR_EN);
+#ifndef LEAVE_DMA_ON
+ ao_arch_critical(
+ if (--ao_dma_active == 0)
+ stm_rcc.ahbenr &= ~(1UL << STM_RCC_AHBENR_DMA1EN);
+ );
+#endif
+ if (ao_dma_allocated[index])
+ ao_dma_mutex[index] = 0;
+ else
+ ao_mutex_put(&ao_dma_mutex[index]);
+}
+
+void
+ao_dma_alloc(uint8_t index)
+{
+ if (ao_dma_allocated[index])
+ ao_panic(AO_PANIC_DMA);
+ ao_dma_allocated[index] = 1;
+}
+
+#if DEBUG
+void
+ao_dma_dump_cmd(void)
+{
+ int i;
+
+#ifndef LEAVE_DMA_ON
+ ao_arch_critical(
+ if (ao_dma_active++ == 0)
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+ );
+#endif
+ printf ("isr %08x ifcr%08x\n", stm_dma.isr, stm_dma.ifcr);
+ for (i = 0; i < NUM_DMA; i++)
+ printf("%d: done %d allocated %d mutex %2d ccr %04x cndtr %04x cpar %08x cmar %08x isr %08x\n",
+ i,
+ ao_dma_done[i],
+ ao_dma_allocated[i],
+ ao_dma_mutex[i],
+ stm_dma.channel[i].ccr,
+ stm_dma.channel[i].cndtr,
+ stm_dma.channel[i].cpar,
+ stm_dma.channel[i].cmar,
+ ao_dma_config[i].isr);
+#ifndef LEAVE_DMA_ON
+ ao_arch_critical(
+ if (--ao_dma_active == 0)
+ stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_DMA1EN);
+ );
+#endif
+}
+
+static const struct ao_cmds ao_dma_cmds[] = {
+ { ao_dma_dump_cmd, "D\0Dump DMA status" },
+ { 0, NULL }
+};
+#endif
+
+void
+ao_dma_init(void)
+{
+ int index;
+
+#ifdef LEAVE_DMA_ON
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+#endif
+ for (index = 0; index < STM_NUM_DMA; index++) {
+#if STM_DMA1_5_STOLEN
+ if (index == STM_DMA_INDEX(5)) {
+ ao_dma_allocated[index] = 1;
+ ao_dma_mutex[index] = 0xff;
+ continue;
+ }
+#endif
+#if STM_DMA1_3_STOLEN
+ if (index == STM_DMA_INDEX(3)) {
+ ao_dma_allocated[index] = 1;
+ ao_dma_mutex[index] = 0xff;
+ continue;
+ }
+#endif
+ stm_nvic_set_enable(STM_ISR_DMA1_CHANNEL1_POS + index);
+ stm_nvic_set_priority(STM_ISR_DMA1_CHANNEL1_POS + index,
+ AO_STM_NVIC_MED_PRIORITY);
+ ao_dma_allocated[index] = 0;
+ ao_dma_mutex[index] = 0;
+ }
+#if DEBUG
+ ao_cmd_register(&ao_dma_cmds[0]);
+#endif
+}
--- /dev/null
+/*
+ * Copyright © 2024 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_flash.h>
+
+extern uint8_t __eeprom_base[2048];
+
+uint8_t
+ao_eeprom_read(ao_pos_t pos, void *v, uint16_t len)
+{
+ memcpy(v, &__eeprom_base[pos], len);
+ return 1;
+}
+
+uint8_t
+ao_eeprom_write(ao_pos_t pos, void *v, uint16_t len)
+{
+ bool hsi_on = (stm_rcc.cr & (1UL << STM_RCC_CR_HSIRDY)) != 0;
+
+ if (!hsi_on) {
+ stm_rcc.cr |= (1UL << STM_RCC_CR_HSION);
+ while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
+ ao_arch_nop();
+
+ }
+ ao_flash_bytes(&__eeprom_base[pos], v, len);
+
+ if (!hsi_on) {
+ stm_rcc.cr &= ~(1UL << STM_RCC_CR_HSION);
+ }
+
+ return 1;
+}
+
--- /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; 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_EXTI_H_
+#define _AO_EXTI_H_
+
+void
+ao_exti_setup(struct stm_gpio *gpio, uint8_t pin, uint8_t mode, void (*callback)(void));
+
+void
+ao_exti_set_mode(struct stm_gpio *gpio, uint8_t pin, uint8_t mode);
+
+void
+ao_exti_set_callback(struct stm_gpio *gpio, uint8_t pin, void (*callback)(void));
+
+void
+ao_exti_enable(struct stm_gpio *gpio, uint8_t pin);
+
+void
+ao_exti_disable(struct stm_gpio *gpio, uint8_t pin);
+
+void
+ao_exti_init(void);
+
+#endif /* _AO_EXTI_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; 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_exti.h>
+
+static void (*ao_exti_callback[16])(void);
+
+uint32_t ao_last_exti;
+
+static void ao_exti_one_isr(uint8_t pin) {
+ uint32_t pending = (ao_last_exti = stm_exti.pr) & (1 << pin);
+
+ stm_exti.pr = pending;
+ if (pending && ao_exti_callback[pin])
+ (*ao_exti_callback[pin])();
+}
+
+static void ao_exti_range_isr(uint8_t first, uint8_t last, uint16_t mask) {
+ uint16_t pending = (uint16_t) (ao_last_exti = stm_exti.pr) & mask;
+ uint8_t pin;
+ static uint16_t last_mask;
+ static uint8_t last_pin;
+
+ if (pending == last_mask) {
+ stm_exti.pr = last_mask;
+ (*ao_exti_callback[last_pin])();
+ return;
+ }
+ stm_exti.pr = pending;
+ for (pin = first; pin <= last; pin++)
+ if ((pending & ((uint32_t) 1 << pin)) && ao_exti_callback[pin]) {
+ last_mask = (1 << pin);
+ last_pin = pin;
+ (*ao_exti_callback[pin])();
+ }
+}
+
+void stm_exti0_isr(void) { ao_exti_one_isr(0); }
+void stm_exti1_isr(void) { ao_exti_one_isr(1); }
+void stm_exti2_isr(void) { ao_exti_one_isr(2); }
+void stm_exti3_isr(void) { ao_exti_one_isr(3); }
+void stm_exti4_isr(void) { ao_exti_one_isr(4); }
+void stm_exti9_5_isr(void) { ao_exti_range_isr(5, 9, 0x3e0); }
+void stm_exti15_10_isr(void) { ao_exti_range_isr(10, 15, 0xfc00); }
+
+void
+ao_exti_setup (struct stm_gpio *gpio, uint8_t pin, uint8_t mode, void (*callback)(void)) {
+ uint32_t mask = 1 << pin;
+ uint8_t irq;
+ uint8_t prio;
+
+ ao_exti_callback[pin] = callback;
+
+ /* configure gpio to interrupt routing */
+ stm_exticr_set(gpio, pin);
+
+ if (!(mode & AO_EXTI_PIN_NOCONFIGURE)) {
+ /* configure pin as input, setting selected pull-up/down mode */
+ ao_enable_input(gpio, pin, mode);
+ }
+
+ /* Set interrupt mask and rising/falling mode */
+ stm_exti.imr &= ~mask;
+ if (mode & AO_EXTI_MODE_RISING)
+ stm_exti.rtsr |= mask;
+ else
+ stm_exti.rtsr &= ~mask;
+ if (mode & AO_EXTI_MODE_FALLING)
+ stm_exti.ftsr |= mask;
+ else
+ stm_exti.ftsr &= ~mask;
+
+ if (pin <= 4)
+ irq = STM_ISR_EXTI0_POS + pin;
+ else if (pin <= 9)
+ irq = STM_ISR_EXTI9_5_POS;
+ else
+ irq = STM_ISR_EXTI15_10_POS;
+
+ /* Set priority */
+ prio = AO_STM_NVIC_MED_PRIORITY;
+ if (mode & AO_EXTI_PRIORITY_LOW)
+ prio = AO_STM_NVIC_LOW_PRIORITY;
+ else if (mode & AO_EXTI_PRIORITY_HIGH)
+ prio = AO_STM_NVIC_HIGH_PRIORITY;
+
+ stm_nvic_set_priority(irq, prio);
+ stm_nvic_set_enable(irq);
+}
+
+void
+ao_exti_set_mode(struct stm_gpio *gpio, uint8_t pin, uint8_t mode) {
+ (void) gpio;
+
+ uint32_t mask = 1 << pin;
+
+ if (mode & AO_EXTI_MODE_RISING)
+ stm_exti.rtsr |= mask;
+ else
+ stm_exti.rtsr &= ~mask;
+ if (mode & AO_EXTI_MODE_FALLING)
+ stm_exti.ftsr |= mask;
+ else
+ stm_exti.ftsr &= ~mask;
+}
+
+void
+ao_exti_set_callback(struct stm_gpio *gpio, uint8_t pin, void (*callback)(void)) {
+ (void) gpio;
+ ao_exti_callback[pin] = callback;
+}
+
+void
+ao_exti_enable(struct stm_gpio *gpio, uint8_t pin) {
+ uint32_t mask = (1 << pin);
+ (void) gpio;
+ stm_exti.pr = mask;
+ stm_exti.imr |= mask;
+}
+
+void
+ao_exti_disable(struct stm_gpio *gpio, uint8_t pin) {
+ uint32_t mask = (1 << pin);
+ (void) gpio;
+ stm_exti.imr &= ~mask;
+ stm_exti.pr = mask;
+}
+
+void
+ao_exti_init(void)
+{
+}
--- /dev/null
+/*
+ * Copyright © 2024 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_fast_timer.h>
+
+static void (*ao_fast_timer_callback[AO_FAST_TIMER_MAX])(void);
+static uint8_t ao_fast_timer_count;
+static uint8_t ao_fast_timer_users;
+
+#if AO_FAST_TIMER == 1
+
+# define AO_FAST_TIMER_TYPE 18
+# define stm_tim stm_tim1
+# define stm_tim_isr stm_tim1_up_isr
+# define STM_ISR_TIM_POS STM_ISR_TIM1_UP_POS
+# define STM_RCC_APBENR_TIMEN STM_RCC_APB2ENR_TIM1EN
+
+#elif AO_FAST_TIMER == 2
+
+# define AO_FAST_TIMER_TYPE 234
+# define stm_tim stm_tim2
+# define stm_tim_isr stm_tim2_isr
+# define STM_ISR_TIM_POS STM_ISR_TIM2_POS
+# define STM_RCC_APBENR_TIMEN STM_RCC_APB1ENR_TIM2EN
+
+#elif AO_FAST_TIMER == 3
+
+# define AO_FAST_TIMER_TYPE 234
+# define stm_tim stm_tim3
+# define stm_tim_isr stm_tim3_isr
+# define STM_ISR_TIM_POS STM_ISR_TIM3_POS
+# define STM_RCC_APBENR_TIMEN STM_RCC_APB1ENR_TIM3EN
+
+#elif AO_FAST_TIMER == 4
+
+# define AO_FAST_TIMER_TYPE 234
+# define stm_tim stm_tim4
+# define stm_tim_isr stm_tim4_isr
+# define STM_ISR_TIM_POS STM_ISR_TIM4_POS
+# define STM_RCC_APBENR_TIMEN STM_RCC_APB1ENR_TIM4EN
+
+#else
+#error AO_FAST_TIMER
+#endif
+
+#if AO_FAST_TIMER_TYPE == 18
+
+#define STM_TIM_CR1(cen) ((0 << STM_TIM18_CR1_CKD) | \
+ (0 << STM_TIM18_CR1_ARPE) | \
+ (0 << STM_TIM18_CR1_CMS) | \
+ (0 << STM_TIM18_CR1_DIR) | \
+ (0 << STM_TIM18_CR1_OPM) | \
+ (1 << STM_TIM18_CR1_URS) | \
+ (0 << STM_TIM18_CR1_UDIS) | \
+ ((cen) << STM_TIM18_CR1_CEN))
+#define STM_TIM_SR_UIF STM_TIM18_SR_UIF
+#define STM_TIM_DIER_UIE STM_TIM18_DIER_UIE
+#define STM_TIM_EGR_UG STM_TIM18_EGR_UG
+#define STM_TIM_CR2_MMS STM_TIM18_CR2_MMS
+#define STM_TIM_CR2_MMS_RESET STM_TIM18_CR2_MMS_RESET
+
+#define AO_TIM_PCLK AO_PCLK2
+
+/*
+ * According to the STM clock-configuration, timers 18 run
+ * twice as fast as the APB2 clock *if* the APB2 prescaler
+ * is greater than 1.
+ */
+
+#if AO_APB2_PRESCALER > 1
+#define AO_TIM_SCALER 2
+#else
+#define AO_TIM_SCALER 1
+#endif
+
+#define STM_RCC_APB_TIM stm_rcc.apb2enr
+
+#elif AO_FAST_TIMER_TYPE == 234
+
+#define STM_TIM_CR1(cen) ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) | \
+ (0 << STM_TIM234_CR1_ARPE) | \
+ (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) | \
+ (0 << STM_TIM234_CR1_DIR) | \
+ (0 << STM_TIM234_CR1_OPM) | \
+ (0 << STM_TIM234_CR1_URS) | \
+ (0 << STM_TIM234_CR1_UDIS) | \
+ ((cen) << STM_TIM234_CR1_CEN)) \
+
+#define AO_TIM_PCLK AO_PCLK1
+
+/*
+ * According to the STM clock-configuration, timers 234 run
+ * twice as fast as the APB1 clock *if* the APB1 prescaler
+ * is greater than 1.
+ */
+
+#if AO_APB1_PRESCALER > 1
+#define AO_TIM_SCALER 2
+#else
+#define AO_TIM_SCALER 1
+#endif
+
+#define STM_TIM_SR_UIF STM_TIM234_SR_UIF
+#define STM_TIM_DIER_UIE STM_TIM234_DIER_UIE
+#define STM_TIM_EGR_UG STM_TIM234_EGR_UG
+#define STM_TIM_CR2_MMS STM_TIM234_CR2_MMS
+#define STM_TIM_CR2_MMS_RESET STM_TIM234_CR2_MMS_RESET
+
+#define STM_RCC_APB_TIM stm_rcc.apb1enr
+
+#endif
+
+static void
+ao_fast_timer_enable(void)
+{
+ stm_tim.cr1 = STM_TIM_CR1(1);
+}
+
+static void
+ao_fast_timer_disable(void)
+{
+ stm_tim.cr1 = STM_TIM_CR1(0);
+}
+
+void
+ao_fast_timer_on(void (*callback)(void))
+{
+ ao_fast_timer_callback[ao_fast_timer_count] = callback;
+ if (!ao_fast_timer_count++)
+ ao_fast_timer_enable();
+}
+
+void
+ao_fast_timer_off(void (*callback)(void))
+{
+ uint8_t n;
+
+ for (n = 0; n < ao_fast_timer_count; n++)
+ if (ao_fast_timer_callback[n] == callback) {
+ for (; n < ao_fast_timer_count-1; n++) {
+ ao_fast_timer_callback[n] = ao_fast_timer_callback[n+1];
+ }
+ if (!--ao_fast_timer_count)
+ ao_fast_timer_disable();
+ break;
+ }
+}
+
+void stm_tim_isr(void)
+{
+ uint8_t i;
+ if (stm_tim.sr & (1 << STM_TIM_SR_UIF)) {
+ stm_tim.sr = 0;
+
+ for (i = 0; i < ao_fast_timer_count; i++)
+ (*ao_fast_timer_callback[i])();
+ }
+}
+
+#ifndef FAST_TIMER_FREQ
+#define FAST_TIMER_FREQ 10000
+#endif
+
+#define TIMER_FAST ((AO_TIM_PCLK * AO_TIM_SCALER) / FAST_TIMER_FREQ)
+
+void
+ao_fast_timer_init(void)
+{
+ if (!ao_fast_timer_users) {
+ stm_nvic_set_enable(STM_ISR_TIM_POS);
+ stm_nvic_set_priority(STM_ISR_TIM_POS, AO_STM_NVIC_CLOCK_PRIORITY);
+
+ /* Turn on timer 1 */
+ STM_RCC_APB_TIM |= (1 << STM_RCC_APBENR_TIMEN);
+
+ stm_tim.psc = TIMER_FAST;
+ stm_tim.arr = 9;
+ stm_tim.cnt = 0;
+
+ /* Enable update interrupt */
+ stm_tim.dier = (1 << STM_TIM_DIER_UIE);
+
+ /* Poke timer to reload values */
+ stm_tim.egr |= (1 << STM_TIM_EGR_UG);
+
+ stm_tim.cr2 = (STM_TIM_CR2_MMS_RESET << STM_TIM_CR2_MMS);
+ ao_fast_timer_disable();
+ }
+ if (ao_fast_timer_users == AO_FAST_TIMER_MAX)
+ ao_panic(AO_PANIC_FAST_TIMER);
+ ao_fast_timer_users++;
+}
+
--- /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; 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_FLASH_STM_H_
+#define _AO_FLASH_STM_H_
+
+void
+ao_flash_erase_page(uint32_t *page);
+
+void
+ao_flash_page(uint32_t *page, uint32_t *src);
+
+void
+ao_flash_bytes(void *page, void *src, size_t size);
+
+#endif /* _AO_FLASH_STM_H_ */
--- /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; 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_exti.h>
+#include <ao_boot.h>
+#include <ao_flash_task.h>
+
+int
+main(void)
+{
+ ao_clock_init();
+
+ ao_usb_init();
+
+#if HAS_TICK
+ ao_timer_init();
+#endif
+
+#ifdef AO_FLASH_LOADER_INIT
+ AO_FLASH_LOADER_INIT;
+#endif
+ ao_flash_task();
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2023 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_flash.h>
+
+/* Note that the HSI clock must be running for this code to work.
+ * Also, special care must be taken with the linker to ensure that the
+ * functions marked 'ramtext' land in ram and not rom. An example of that
+ * can be found in altos-loader.ld
+ */
+
+static uint8_t
+ao_flash_is_locked(void)
+{
+ return (stm_flash.cr & (1 << STM_FLASH_CR_LOCK)) != 0;
+}
+
+static void
+ao_flash_unlock(void)
+{
+ if (!ao_flash_is_locked())
+ return;
+
+ /* Unlock FLASH_CR register */
+ stm_flash.keyr = STM_FLASH_KEYR_KEY1;
+ stm_flash.keyr = STM_FLASH_KEYR_KEY2;
+ if (ao_flash_is_locked())
+ ao_panic(AO_PANIC_FLASH);
+}
+
+static void
+ao_flash_lock(void)
+{
+ stm_flash.cr |= (1 << STM_FLASH_CR_LOCK);
+}
+
+#define ao_flash_wait_bsy() do { while (stm_flash.sr & (1 << STM_FLASH_SR_BSY)); } while (0)
+
+static void __attribute__ ((section(".sdata2.flash"), noinline))
+_ao_flash_erase_page(uint32_t *page)
+{
+ stm_flash.cr |= (1 << STM_FLASH_CR_PER);
+
+ stm_flash.ar = (uintptr_t) page;
+
+ stm_flash.cr |= (1 << STM_FLASH_CR_STRT);
+
+ ao_flash_wait_bsy();
+
+ stm_flash.cr &= ~(1UL << STM_FLASH_CR_PER);
+}
+
+static uint32_t
+stm_flash_page_size(void)
+{
+ uint16_t f_size = stm_flash_data.f_size;
+
+ if (f_size <= 128) {
+ /* low-density and medium-density devices */
+ return 1024;
+ } else {
+ /*
+ * high-density devices, XL-density devices and
+ * Connectivity devices
+ */
+ return 2048;
+ }
+}
+
+void
+ao_flash_erase_page(uint32_t *page)
+{
+ /* Erase the whole page at the start. This assumes we'll be flashing things
+ * in memory order
+ */
+
+ if ((uintptr_t) page & (stm_flash_page_size() - 1))
+ return;
+
+ ao_arch_block_interrupts();
+ ao_flash_unlock();
+
+ _ao_flash_erase_page(page);
+
+ ao_flash_lock();
+ ao_arch_release_interrupts();
+}
+
+static void __attribute__ ((section(".sdata2.flash"), noinline))
+_ao_flash_page(uint16_t *dst, uint16_t *src, unsigned int shorts)
+{
+ uint8_t i;
+
+ stm_flash.cr |= (1 << STM_FLASH_CR_PG);
+
+ for (i = 0; i < shorts; i++) {
+ *dst++ = *src++;
+ ao_flash_wait_bsy();
+ }
+
+ stm_flash.cr &= ~(1UL << STM_FLASH_CR_PG);
+}
+
+void
+ao_flash_page(uint32_t *page, uint32_t *src)
+{
+ ao_flash_erase_page(page);
+
+ ao_arch_block_interrupts();
+ ao_flash_unlock();
+
+ _ao_flash_page((uint16_t *) page, (uint16_t *) src, 128);
+
+ ao_flash_lock();
+ ao_arch_release_interrupts();
+}
+
+/* Stores less than a full page while still smashing the full page */
+void
+ao_flash_bytes(void *page, void *src, size_t size)
+{
+ unsigned int shorts = (unsigned int) ((size + 1) >> 1);
+
+ ao_flash_erase_page(page);
+
+ ao_arch_block_interrupts();
+ ao_flash_unlock();
+
+ _ao_flash_page((uint16_t *) page, (uint16_t *) src, shorts);
+
+ ao_flash_lock();
+ ao_arch_release_interrupts();
+}
--- /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; 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_FLASH_STM_PINS_H_
+#define _AO_FLASH_STM_PINS_H_
+
+#include <ao_flash_pins.h>
+
+#ifndef AO_SYSCLK
+
+#define AO_SYSCLK 72000000
+#define AO_HCLK 72000000
+#define AO_APB1CLK 36000000
+#define AO_APB2CLK 72000000
+#define AO_ADCCLK 12000000
+
+#define AO_RCC_CFGR_USBPRE STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE STM_RCC_CFGR_PLLXTPRE_1
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE1_DIV_2
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+#define AO_RCC_CFGR_ADCPRE STM_RCC_CFGR_ADCPRE_6
+
+#endif
+
+#endif /* _AO_FLASH_STM_PINS_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; 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>
+
+struct ao_i2c_stm_info {
+ uint8_t tx_dma_index;
+ uint8_t rx_dma_index;
+ struct stm_i2c *stm_i2c;
+};
+
+#define I2C_FAST 1
+
+#define I2C_TIMEOUT 100
+
+#define I2C_IDLE 0
+#define I2C_RUNNING 1
+#define I2C_ERROR 2
+
+static uint8_t ao_i2c_state[STM_NUM_I2C];
+static uint16_t ao_i2c_addr[STM_NUM_I2C];
+uint8_t ao_i2c_mutex[STM_NUM_I2C];
+
+# define I2C_HIGH_SLOW 5000 /* ns, 100kHz clock */
+#ifdef TELEMEGA
+# define I2C_HIGH_FAST 2000 /* ns, 167kHz clock */
+#else
+# define I2C_HIGH_FAST 1000 /* ns, 333kHz clock */
+#endif
+
+# define I2C_RISE_SLOW 500 /* ns */
+# define I2C_RISE_FAST 100 /* ns */
+
+/* Clock period in ns */
+#define CYCLES(period) (((period) * (AO_PCLK1 / 1000)) / 1000000)
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#define I2C_CCR_HIGH_SLOW max(4,CYCLES(I2C_HIGH_SLOW))
+#define I2C_CCR_HIGH_FAST max(4,CYCLES(I2C_HIGH_FAST))
+#define I2C_TRISE_SLOW (CYCLES(I2C_RISE_SLOW) + 1)
+#define I2C_TRISE_FAST (CYCLES(I2C_RISE_FAST) + 1)
+
+#if I2C_FAST
+#define I2C_TRISE I2C_TRISE_FAST
+#define I2C_CCR_HIGH I2C_CCR_HIGH_FAST
+#else
+#define I2C_TRISE I2C_TRISE_SLOW
+#define I2C_CCR_HIGH I2C_CCR_HIGH_SLOW
+#endif
+
+#define AO_STM_I2C_CR2_FREQ (AO_APB1CLK / 1000000)
+
+#define AO_STM_I2C_CR1 ((0 << STM_I2C_CR1_SWRST) | \
+ (0 << STM_I2C_CR1_ALERT) | \
+ (0 << STM_I2C_CR1_PEC) | \
+ (0 << STM_I2C_CR1_POS) | \
+ (0 << STM_I2C_CR1_ACK) | \
+ (0 << STM_I2C_CR1_STOP) | \
+ (0 << STM_I2C_CR1_START) | \
+ (0 << STM_I2C_CR1_NOSTRETCH) | \
+ (0 << STM_I2C_CR1_ENGC) | \
+ (0 << STM_I2C_CR1_ENPEC) | \
+ (0 << STM_I2C_CR1_ENARP) | \
+ (0 << STM_I2C_CR1_SMBTYPE) | \
+ (0 << STM_I2C_CR1_SMBUS) | \
+ (1 << STM_I2C_CR1_PE))
+
+#define AO_STM_I2C_CR2 ((0 << STM_I2C_CR2_LAST) | \
+ (0 << STM_I2C_CR2_DMAEN) | \
+ (0 << STM_I2C_CR2_ITBUFEN) | \
+ (0 << STM_I2C_CR2_ITEVTEN) | \
+ (0 << STM_I2C_CR2_ITERREN) | \
+ (AO_STM_I2C_CR2_FREQ << STM_I2C_CR2_FREQ))
+
+static const struct ao_i2c_stm_info ao_i2c_stm_info[STM_NUM_I2C] = {
+ {
+ .tx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C1_TX),
+ .rx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C1_RX),
+ .stm_i2c = &stm_i2c1
+ },
+ {
+ .tx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C2_TX),
+ .rx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C2_RX),
+ .stm_i2c = &stm_i2c2
+ },
+};
+
+static uint8_t *ao_i2c_recv_data[STM_NUM_I2C];
+static uint16_t ao_i2c_recv_len[STM_NUM_I2C];
+static uint16_t ev_count;
+
+static void
+ao_i2c_ev_isr(uint8_t index)
+{
+ struct stm_i2c *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+ uint32_t sr1;
+
+ ++ev_count;
+ sr1 = stm_i2c->sr1;
+ if (sr1 & (1 << STM_I2C_SR1_SB))
+ stm_i2c->dr = ao_i2c_addr[index];
+ if (sr1 & (1 << STM_I2C_SR1_ADDR)) {
+ stm_i2c->cr2 &= ~(1UL << STM_I2C_CR2_ITEVTEN);
+ ao_i2c_state[index] = I2C_RUNNING;
+ ao_wakeup(&ao_i2c_state[index]);
+ }
+ if (sr1 & (1 << STM_I2C_SR1_BTF)) {
+ stm_i2c->cr2 &= ~(1UL << STM_I2C_CR2_ITEVTEN);
+ ao_wakeup(&ao_i2c_state[index]);
+ }
+ if (sr1 & (1 << STM_I2C_SR1_RXNE)) {
+ if (ao_i2c_recv_len[index]) {
+ *(ao_i2c_recv_data[index]++) = (uint8_t) stm_i2c->dr;
+ if (!--ao_i2c_recv_len[index])
+ ao_wakeup(&ao_i2c_recv_len[index]);
+ }
+ }
+}
+
+void stm_i2c1_ev_isr(void) { ao_i2c_ev_isr(0); }
+void stm_i2c2_ev_isr(void) { ao_i2c_ev_isr(1); }
+
+static void
+ao_i2c_er_isr(uint8_t index)
+{
+ struct stm_i2c *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+ uint32_t sr1;
+
+ sr1 = stm_i2c->sr1;
+ if (sr1 & (1 << STM_I2C_SR1_AF)) {
+ ao_i2c_state[index] = I2C_ERROR;
+ stm_i2c->sr1 = sr1 & ~(1UL << STM_I2C_SR1_AF);
+ ao_wakeup(&ao_i2c_state[index]);
+ }
+}
+
+void stm_i2c1_er_isr(void) { ao_i2c_er_isr(0); }
+void stm_i2c2_er_isr(void) { ao_i2c_er_isr(1); }
+
+void
+ao_i2c_get(uint8_t index)
+{
+ struct stm_i2c *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+ ao_mutex_get(&ao_i2c_mutex[index]);
+
+ stm_i2c->sr1 = 0;
+ stm_i2c->sr2 = 0;
+}
+
+void
+ao_i2c_put(uint8_t index)
+{
+ ao_mutex_put(&ao_i2c_mutex[index]);
+}
+
+uint8_t
+ao_i2c_start(uint8_t index, uint16_t addr)
+{
+ struct stm_i2c *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+ int t;
+
+ ao_i2c_state[index] = I2C_IDLE;
+ ao_i2c_addr[index] = addr;
+ stm_i2c->cr2 = AO_STM_I2C_CR2;
+ stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_START);
+ for (t = 0; t < I2C_TIMEOUT; t++) {
+ if (!(stm_i2c->cr1 & (1 << STM_I2C_CR1_START)))
+ break;
+ }
+ ao_arch_block_interrupts();
+ stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_ITEVTEN) | (1 << STM_I2C_CR2_ITERREN);
+ ao_i2c_ev_isr(index);
+ while (ao_i2c_state[index] == I2C_IDLE)
+ if (ao_sleep_for(&ao_i2c_state[index], AO_MS_TO_TICKS(250)))
+ break;
+ ao_arch_release_interrupts();
+ return ao_i2c_state[index] == I2C_RUNNING;
+}
+
+static void
+ao_i2c_wait_stop(uint8_t index)
+{
+ struct stm_i2c *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+ int t;
+
+ for (t = 0; t < I2C_TIMEOUT; t++) {
+ if (!(stm_i2c->cr1 & (1 << STM_I2C_CR1_STOP)))
+ break;
+ ao_yield();
+ }
+ ao_i2c_state[index] = I2C_IDLE;
+}
+
+static void
+ao_i2c_wait_addr(uint8_t index)
+{
+ struct stm_i2c *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+ int t;
+
+ for (t = 0; t < I2C_TIMEOUT; t++)
+ if (!(stm_i2c->sr1 & (1 << STM_I2C_SR1_ADDR)))
+ break;
+ if (t)
+ printf ("wait_addr %d\n", t);
+}
+
+uint8_t
+ao_i2c_send(void *block, uint16_t len, uint8_t index, uint8_t stop)
+{
+ struct stm_i2c *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+ uint8_t tx_dma_index = ao_i2c_stm_info[index].tx_dma_index;
+ uint8_t rx_dma_index = ao_i2c_stm_info[index].rx_dma_index;
+
+ /* Clear any pending ADDR bit */
+ (void) stm_i2c->sr2;
+ ao_i2c_wait_addr(index);
+ ao_dma_set_transfer(tx_dma_index,
+ &stm_i2c->dr,
+ block,
+ len,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+ (1 << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+ ao_dma_mutex_get(rx_dma_index);
+ stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_DMAEN);
+
+ ao_dma_start(tx_dma_index);
+ ao_arch_block_interrupts();
+ while (!ao_dma_done[tx_dma_index])
+ if (ao_sleep_for(&ao_dma_done[tx_dma_index], 1 + len))
+ break;
+ stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_ITEVTEN) | (1 << STM_I2C_CR2_ITERREN);
+ while ((stm_i2c->sr1 & (1 << STM_I2C_SR1_BTF)) == 0)
+ if (ao_sleep_for(&ao_i2c_state[index], 1 + len))
+ break;
+ stm_i2c->cr2 = AO_STM_I2C_CR2;
+ ao_arch_release_interrupts();
+ if (stop) {
+ stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP);
+ ao_i2c_wait_stop(index);
+ }
+ ao_dma_mutex_put(rx_dma_index);
+ ao_dma_done_transfer(tx_dma_index);
+ return true;
+}
+
+static void
+ao_i2c_recv_dma_isr(int index)
+{
+ int i;
+ struct stm_i2c *stm_i2c = NULL;
+
+ for (i = 0; i < STM_NUM_I2C; i++)
+ if (index == ao_i2c_stm_info[i].rx_dma_index) {
+ stm_i2c = ao_i2c_stm_info[i].stm_i2c;
+ break;
+ }
+ if (!stm_i2c)
+ return;
+ stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_LAST);
+ ao_dma_done[index] = 1;
+ ao_wakeup(&ao_dma_done[index]);
+}
+
+uint8_t
+ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop)
+{
+ struct stm_i2c *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+ uint8_t ret = true;
+
+ if (len == 0)
+ return true;
+ if (len == 1) {
+ ao_i2c_recv_data[index] = block;
+ ao_i2c_recv_len[index] = 1;
+ stm_i2c->cr1 = AO_STM_I2C_CR1;
+
+ /* Clear any pending ADDR bit */
+ stm_i2c->sr2;
+ ao_i2c_wait_addr(index);
+
+ /* Enable interrupts to transfer the byte */
+ stm_i2c->cr2 = (AO_STM_I2C_CR2 |
+ (1 << STM_I2C_CR2_ITEVTEN) |
+ (1 << STM_I2C_CR2_ITERREN) |
+ (1 << STM_I2C_CR2_ITBUFEN));
+ if (stop)
+ stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP);
+
+ ao_arch_block_interrupts();
+ while (ao_i2c_recv_len[index])
+ if (ao_sleep_for(&ao_i2c_recv_len[index], 1))
+ break;
+ ao_arch_release_interrupts();
+ ret = ao_i2c_recv_len[index] == 0;
+ } else {
+ uint8_t tx_dma_index = ao_i2c_stm_info[index].tx_dma_index;
+ uint8_t rx_dma_index = ao_i2c_stm_info[index].rx_dma_index;
+ ao_dma_mutex_get(tx_dma_index);
+ ao_dma_set_transfer(rx_dma_index,
+ &stm_i2c->dr,
+ block,
+ len,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+ (1 << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+
+ /* XXX ao_i2c_recv_dma_isr hasn't ever been used, so it
+ * doesn't appear to be necessary. Testing with a device
+ * that uses i2c would really be useful here to discover
+ * whether this function is necessary or not.
+ */
+#if 0
+ ao_dma_set_isr(rx_dma_index, ao_i2c_recv_dma_isr);
+#else
+ (void) ao_i2c_recv_dma_isr;
+#endif
+ stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_ACK);
+ stm_i2c->cr2 = AO_STM_I2C_CR2 |
+ (1 << STM_I2C_CR2_DMAEN) | (1 << STM_I2C_CR2_LAST);
+ /* Clear any pending ADDR bit */
+ (void) stm_i2c->sr2;
+ ao_i2c_wait_addr(index);
+
+ ao_dma_start(rx_dma_index);
+ ao_arch_block_interrupts();
+ while (!ao_dma_done[rx_dma_index])
+ if (ao_sleep_for(&ao_dma_done[rx_dma_index], len))
+ break;
+ ao_arch_release_interrupts();
+ ret = ao_dma_done[rx_dma_index];
+ stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP);
+ ao_dma_done_transfer(rx_dma_index);
+ ao_dma_mutex_put(tx_dma_index);
+ }
+ if (stop)
+ ao_i2c_wait_stop(index);
+ return ret;
+}
+
+static void
+ao_i2c_channel_init(uint8_t index)
+{
+ struct stm_i2c *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+ int i;
+
+ /* Turn I2C off while configuring */
+ stm_i2c->cr1 = (1 << STM_I2C_CR1_SWRST);
+ for (i = 0; i < 100; i++)
+ asm("nop");
+ stm_i2c->cr1 = 0;
+ stm_i2c->cr2 = AO_STM_I2C_CR2;
+
+ (void) stm_i2c->sr1;
+ (void) stm_i2c->sr2;
+ (void) stm_i2c->dr;
+
+ stm_i2c->sr1 = 0;
+ stm_i2c->sr2 = 0;
+
+ stm_i2c->ccr = ((I2C_FAST << STM_I2C_CCR_FS) |
+ (0 << STM_I2C_CCR_DUTY) |
+ (I2C_CCR_HIGH << STM_I2C_CCR_CCR));
+
+ stm_i2c->trise = I2C_TRISE;
+
+ stm_i2c->cr1 = AO_STM_I2C_CR1;
+}
+
+static inline void
+i2c_pin_set(struct stm_gpio *gpio, int pin)
+{
+ ao_enable_port(gpio);
+ stm_gpio_conf(gpio, pin,
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_AF_OPEN_DRAIN);
+}
+
+void
+ao_i2c_init(void)
+{
+#if HAS_I2C_1
+# if I2C_1_PB6_PB7
+ stm_set_afio_mapr(STM_AFIO_MAPR_I2C1_REMAP,
+ STM_AFIO_MAPR_I2C1_REMAP_PB6_PB7,
+ STM_AFIO_MAPR_I2C1_REMAP_MASK);
+ i2c_pin_set(&stm_gpiob, 6);
+ i2c_pin_set(&stm_gpiob, 7);
+# else
+# if I2C_1_PB8_PB9
+ stm_set_afio_mapr(STM_AFIO_MAPR_I2C1_REMAP,
+ STM_AFIO_MAPR_I2C1_REMAP_PB8_PB9,
+ STM_AFIO_MAPR_I2C1_REMAP_MASK);
+ i2c_pin_set(&stm_gpiob, 8);
+ i2c_pin_set(&stm_gpiob, 9);
+# else
+# error "No I2C_1 port configuration specified"
+# endif
+# endif
+
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_I2C1EN);
+ ao_i2c_channel_init(0);
+
+ stm_nvic_set_enable(STM_ISR_I2C1_EV_POS);
+ stm_nvic_set_priority(STM_ISR_I2C1_EV_POS, AO_STM_NVIC_MED_PRIORITY);
+ stm_nvic_set_enable(STM_ISR_I2C1_ER_POS);
+ stm_nvic_set_priority(STM_ISR_I2C1_ER_POS, AO_STM_NVIC_MED_PRIORITY);
+#endif
+
+#if HAS_I2C_2
+# if I2C_2_PB10_PB11
+ i2c_pin_set(&stm_gpiob, 10);
+ i2c_pin_set(&stm_gpiob, 11);
+# else
+# error "No I2C_2 port configuration specified"
+# endif
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_I2C2EN);
+ ao_i2c_channel_init(1);
+
+ stm_nvic_set_enable(STM_ISR_I2C2_EV_POS);
+ stm_nvic_set_priority(STM_ISR_I2C2_EV_POS, AO_STM_NVIC_MED_PRIORITY);
+ stm_nvic_set_enable(STM_ISR_I2C2_ER_POS);
+ stm_nvic_set_priority(STM_ISR_I2C2_ER_POS, AO_STM_NVIC_MED_PRIORITY);
+#endif
+}
--- /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; 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 "stm32f1.h"
+#include <string.h>
+#include <ao_boot.h>
+
+extern void main(void);
+
+/* Interrupt functions */
+
+void stm_halt_isr(void)
+{
+ ao_panic(AO_PANIC_CRASH);
+}
+
+void stm_ignore_isr(void)
+{
+}
+
+#define STRINGIFY(x) #x
+
+#define isr(name) \
+ void __attribute__ ((weak)) stm_ ## name ## _isr(void); \
+ _Pragma(STRINGIFY(weak stm_ ## name ## _isr = stm_ignore_isr))
+
+#define isr_halt(name) \
+ void __attribute__ ((weak)) stm_ ## name ## _isr(void); \
+ _Pragma(STRINGIFY(weak stm_ ## name ## _isr = stm_halt_isr))
+
+isr(nmi);
+isr_halt(hardfault);
+isr_halt(memmanage);
+isr_halt(busfault);
+isr_halt(usagefault);
+isr(svc);
+isr(debugmon);
+isr(pendsv);
+isr(systick);
+isr(wwdg);
+isr(pvd);
+isr(tamper_stamp);
+isr(rtc_wkup);
+isr(flash);
+isr(rcc);
+isr(exti0);
+isr(exti1);
+isr(exti2);
+isr(exti3);
+isr(exti4);
+isr(dma1_channel1);
+isr(dma1_channel2);
+isr(dma1_channel3);
+isr(dma1_channel4);
+isr(dma1_channel5);
+isr(dma1_channel6);
+isr(dma1_channel7);
+isr(adc1_2);
+isr(usb_hp);
+isr(usb_lp);
+isr(can_rx1);
+isr(can_sce);
+isr(exti9_5);
+isr(tim1_brk);
+isr(tim1_up);
+isr(tim1_trg_com);
+isr(tim1_cc);
+isr(tim2);
+isr(tim3);
+isr(tim4);
+isr(i2c1_ev);
+isr(i2c1_er);
+isr(i2c2_ev);
+isr(i2c2_er);
+isr(spi1);
+isr(spi2);
+isr(usart1);
+isr(usart2);
+isr(usart3);
+isr(exti15_10);
+isr(rtc_alarm);
+isr(usb_wakeup);
+isr(tim8_brk);
+isr(tim8_up);
+isr(tim8_trg_com);
+isr(tim8_cc);
+isr(adc3);
+isr(fsmc);
+isr(sdio);
+isr(tim5);
+isr(spi3);
+isr(uart4);
+isr(uart5);
+isr(tim6);
+isr(tim7);
+isr(dma2_channel1);
+isr(dma2_channel2);
+isr(dma2_channel3);
+isr(dma2_channel4_5);
+
+#define i(addr,name) [(addr)/4] = stm_ ## name ## _isr
+
+extern char __stack[];
+void _start(void) __attribute__((__noreturn__));
+void main(void) __attribute__((__noreturn__));
+void ao_setup(void) __attribute__((constructor));
+
+/* This must be exactly 304 bytes long so that the configuration data
+ * gets loaded at the right place
+ */
+
+__attribute__ ((section(".init")))
+const void * const __interrupt_vector[76] = {
+ [0] = &__stack,
+ [1] = _start,
+ i(0x08, nmi),
+ i(0x0c, hardfault),
+ i(0x10, memmanage),
+ i(0x14, busfault),
+ i(0x18, usagefault),
+ i(0x2c, svc),
+ i(0x30, debugmon),
+ i(0x38, pendsv),
+ i(0x3c, systick),
+ i(0x40, wwdg),
+ i(0x44, pvd),
+ i(0x48, tamper_stamp),
+ i(0x4c, rtc_wkup),
+ i(0x50, flash),
+ i(0x54, rcc),
+ i(0x58, exti0),
+ i(0x5c, exti1),
+ i(0x60, exti2),
+ i(0x64, exti3),
+ i(0x68, exti4),
+ i(0x6c, dma1_channel1),
+ i(0x70, dma1_channel2),
+ i(0x74, dma1_channel3),
+ i(0x78, dma1_channel4),
+ i(0x7c, dma1_channel5),
+ i(0x80, dma1_channel6),
+ i(0x84, dma1_channel7),
+ i(0x88, adc1_2),
+ i(0x8c, usb_hp),
+ i(0x90, usb_lp),
+ i(0x94, can_rx1),
+ i(0x98, can_sce),
+ i(0x9c, exti9_5),
+ i(0xa0, tim1_brk),
+ i(0xa4, tim1_up),
+ i(0xa8, tim1_trg_com),
+ i(0xac, tim1_cc),
+ i(0xb0, tim2),
+ i(0xb4, tim3),
+ i(0xb8, tim4),
+ i(0xbc, i2c1_ev),
+ i(0xc0, i2c1_er),
+ i(0xc4, i2c2_ev),
+ i(0xc8, i2c2_er),
+ i(0xcc, spi1),
+ i(0xd0, spi2),
+ i(0xd4, usart1),
+ i(0xd8, usart2),
+ i(0xdc, usart3),
+ i(0xe0, exti15_10),
+ i(0xe4, rtc_alarm),
+ i(0xe8, usb_wakeup),
+ i(0xec, tim8_brk),
+ i(0xf0, tim8_up),
+ i(0xf4, tim8_trg_com),
+ i(0xf8, tim8_cc),
+ i(0xfc, adc3),
+ i(0x100, fsmc),
+ i(0x104, sdio),
+ i(0x108, tim5),
+ i(0x10c, spi3),
+ i(0x110, uart4),
+ i(0x114, uart5),
+ i(0x118, tim6),
+ i(0x11c, tim7),
+ i(0x120, dma2_channel1),
+ i(0x124, dma2_channel2),
+ i(0x128, dma2_channel3),
+ i(0x12c, dma2_channel4_5),
+};
+
+void __attribute__((constructor)) ao_setup(void) {
+#ifdef AO_BOOT_CHAIN
+ if (ao_boot_check_chain()) {
+#ifdef AO_BOOT_PIN
+ if (ao_boot_check_pin())
+#endif
+ {
+ ao_boot_chain(AO_BOOT_APPLICATION_BASE);
+ }
+ }
+#endif
+ /* Set interrupt vector table offset */
+ stm_scb.vtor = (uint32_t) &__interrupt_vector;
+}
--- /dev/null
+/*
+ * Copyright © 2024 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_pwm.h"
+
+static uint8_t pwm_running;
+
+static uint16_t pwm_value[NUM_PWM];
+
+static void
+ao_pwm_up(void)
+{
+ if (pwm_running++ == 0) {
+ struct stm_tim234 *tim = &AO_PWM_TIMER;
+
+ tim->ccr1 = 0;
+ tim->ccr2 = 0;
+ tim->ccr3 = 0;
+ tim->ccr4 = 0;
+ tim->arr = PWM_MAX - 1; /* turn on the timer */
+ tim->cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+ (0 << STM_TIM234_CR1_ARPE) |
+ (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+ (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+ (0 << STM_TIM234_CR1_OPM) |
+ (0 << STM_TIM234_CR1_URS) |
+ (0 << STM_TIM234_CR1_UDIS) |
+ (1 << STM_TIM234_CR1_CEN));
+
+ /* Set the timer running */
+ tim->egr = (1 << STM_TIM234_EGR_UG);
+ }
+}
+
+static void
+ao_pwm_down(void)
+{
+ if (--pwm_running == 0) {
+ struct stm_tim234 *tim = &AO_PWM_TIMER;
+
+ tim->arr = 0;
+ tim->cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+ (0 << STM_TIM234_CR1_ARPE) |
+ (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+ (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+ (0 << STM_TIM234_CR1_OPM) |
+ (0 << STM_TIM234_CR1_URS) |
+ (0 << STM_TIM234_CR1_UDIS) |
+ (0 << STM_TIM234_CR1_CEN));
+
+ /* Stop the timer */
+ tim->egr = (1 << STM_TIM234_EGR_UG);
+ }
+}
+
+void
+ao_pwm_set(uint8_t pwm, uint16_t value)
+{
+ struct stm_tim234 *tim = &AO_PWM_TIMER;
+
+#if PWM_MAX < UINT16_MAX
+ if (value > PWM_MAX)
+ value = PWM_MAX;
+#endif
+ if (value != 0) {
+ if (pwm_value[pwm] == 0)
+ ao_pwm_up();
+ }
+ switch (pwm) {
+ case 0:
+ tim->ccr1 = value;
+ break;
+ case 1:
+ tim->ccr2 = value;
+ break;
+ case 2:
+ tim->ccr3 = value;
+ break;
+ case 3:
+ tim->ccr4 = value;
+ break;
+ }
+ if (value == 0) {
+ if (pwm_value[pwm] != 0)
+ ao_pwm_down();
+ }
+ pwm_value[pwm] = value;
+}
+
+static void
+ao_pwm_cmd(void)
+{
+ uint8_t ch;
+ uint16_t val;
+
+ ch = (uint8_t) ao_cmd_decimal();
+ val = (uint16_t) ao_cmd_decimal();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+
+ printf("Set channel %d to %d\n", ch, val);
+ ao_pwm_set(ch, val);
+}
+
+static const struct ao_cmds ao_pwm_cmds[] = {
+ { ao_pwm_cmd, "P <ch> <val>\0Set PWM ch to val" },
+ { 0, NULL },
+};
+
+void
+ao_pwm_init(void)
+{
+ struct stm_tim234 *tim = &AO_PWM_TIMER;
+
+ stm_rcc.apb1enr |= (1 << AO_PWM_TIMER_ENABLE);
+
+ tim->cr1 = 0;
+ tim->psc = AO_PWM_TIMER_SCALE - 1;
+ tim->cnt = 0;
+ tim->ccer = ((1 << STM_TIM234_CCER_CC1E) |
+ (0 << STM_TIM234_CCER_CC1P) |
+ (1 << STM_TIM234_CCER_CC2E) |
+ (0 << STM_TIM234_CCER_CC2P) |
+ (1 << STM_TIM234_CCER_CC3E) |
+ (0 << STM_TIM234_CCER_CC3P) |
+ (1 << STM_TIM234_CCER_CC4E) |
+ (0 << STM_TIM234_CCER_CC4P));
+
+ tim->ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
+ (STM_TIM234_CCMR1_OC2M_PWM_MODE_1 << STM_TIM234_CCMR1_OC2M) |
+ (0 << STM_TIM234_CCMR1_OC2PE) |
+ (0 << STM_TIM234_CCMR1_OC2FE) |
+ (STM_TIM234_CCMR1_CC2S_OUTPUT << STM_TIM234_CCMR1_CC2S) |
+
+ (0 << STM_TIM234_CCMR1_OC1CE) |
+ (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M) |
+ (0 << STM_TIM234_CCMR1_OC1PE) |
+ (0 << STM_TIM234_CCMR1_OC1FE) |
+ (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
+
+
+ tim->ccmr2 = ((0 << STM_TIM234_CCMR2_OC4CE) |
+ (STM_TIM234_CCMR2_OC4M_PWM_MODE_1 << 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_PWM_MODE_1 << STM_TIM234_CCMR2_OC3M) |
+ (0 << STM_TIM234_CCMR2_OC3PE) |
+ (0 << STM_TIM234_CCMR2_OC3FE) |
+ (STM_TIM234_CCMR2_CC3S_OUTPUT << STM_TIM234_CCMR2_CC3S));
+ tim->egr = 0;
+
+ tim->sr = 0;
+ tim->dier = 0;
+ tim->smcr = 0;
+ tim->cr2 = ((0 << STM_TIM234_CR2_TI1S) |
+ (STM_TIM234_CR2_MMS_RESET<< STM_TIM234_CR2_MMS) |
+ (0 << STM_TIM234_CR2_CCDS));
+
+ stm_set_afio_mapr(AO_AFIO_PWM_REMAP,
+ AO_AFIO_PWM_REMAP_VAL,
+ AO_AFIO_PWM_REMAP_MASK);
+
+ ao_enable_port(AO_PWM_0_GPIO);
+
+ stm_gpio_conf(AO_PWM_0_GPIO, AO_PWM_0_PIN,
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+#if NUM_PWM > 1
+ stm_gpio_conf(AO_PWM_1_GPIO, AO_PWM_1_PIN,
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+#endif
+#if NUM_PWM > 2
+ stm_gpio_conf(AO_PWM_2_GPIO, AO_PWM_2_PIN,
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+#endif
+#if NUM_PWM > 3
+ stm_gpio_conf(AO_PWM_3_GPIO, AO_PWM_3_PIN,
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+#endif
+ ao_cmd_register(&ao_pwm_cmds[0]);
+}
--- /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; 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_exti.h>
+
+void
+ao_debug_out(char c)
+{
+ if (c == '\n')
+ ao_debug_out('\r');
+ while (!(stm_usart1.sr & (1 << STM_USART_SR_TXE)));
+ stm_usart1.dr = c;
+}
+
+static volatile uint8_t tx;
+
+static int
+_ao_usart_tx_start(struct ao_stm_usart *usart)
+{
+ if (!ao_fifo_empty(usart->tx_fifo)) {
+#if HAS_SERIAL_SW_FLOW
+ if (usart->gpio_cts && ao_gpio_get(usart->gpio_cts, usart->pin_cts) == 1) {
+ ao_exti_enable(usart->gpio_cts, usart->pin_cts);
+ return 0;
+ }
+#endif
+ if (usart->reg->sr & (1 << STM_USART_SR_TXE))
+ {
+ usart->tx_running = 1;
+ usart->reg->cr1 |= (1 << STM_USART_CR1_TXEIE) | (1 << STM_USART_CR1_TCIE);
+ ao_fifo_remove(usart->tx_fifo, tx);
+ usart->reg->dr = tx;
+ ao_wakeup(&usart->tx_fifo);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#if HAS_SERIAL_SW_FLOW
+static void
+_ao_usart_cts(struct ao_stm_usart *usart)
+{
+ if (_ao_usart_tx_start(usart))
+ ao_exti_disable(usart->gpio_cts, usart->pin_cts);
+}
+#endif
+
+static void
+_ao_usart_rx(struct ao_stm_usart *usart, int is_stdin)
+{
+ if (usart->reg->sr & (1 << STM_USART_SR_RXNE)) {
+ if (!ao_fifo_full(usart->rx_fifo)) {
+ ao_fifo_insert(usart->rx_fifo, (char) usart->reg->dr);
+ ao_wakeup(&usart->rx_fifo);
+ if (is_stdin)
+ ao_wakeup(&ao_stdin_ready);
+#if HAS_SERIAL_SW_FLOW
+ /* If the fifo is nearly full, turn off RTS and wait
+ * for it to drain a bunch
+ */
+ if (usart->gpio_rts && ao_fifo_mostly(usart->rx_fifo)) {
+ ao_gpio_set(usart->gpio_rts, usart->pin_rts, 1);
+ usart->rts = 0;
+ }
+#endif
+ } else {
+ usart->reg->cr1 &= ~(1UL << STM_USART_CR1_RXNEIE);
+ }
+ }
+}
+
+static void
+ao_usart_isr(struct ao_stm_usart *usart, int is_stdin)
+{
+ _ao_usart_rx(usart, is_stdin);
+
+ if (!_ao_usart_tx_start(usart))
+ usart->reg->cr1 &= ~(1UL << STM_USART_CR1_TXEIE);
+
+ if (usart->reg->sr & (1 << STM_USART_SR_TC)) {
+ usart->tx_running = 0;
+ usart->reg->cr1 &= ~(1UL << STM_USART_CR1_TCIE);
+ if (usart->draining) {
+ usart->draining = 0;
+ ao_wakeup(&usart->tx_fifo);
+ }
+ }
+}
+
+static int
+_ao_usart_pollchar(struct ao_stm_usart *usart)
+{
+ int c;
+
+ if (ao_fifo_empty(usart->rx_fifo))
+ c = AO_READ_AGAIN;
+ else {
+ uint8_t u;
+ ao_fifo_remove(usart->rx_fifo,u);
+ if ((usart->reg->cr1 & (1 << STM_USART_CR1_RXNEIE)) == 0) {
+ if (ao_fifo_barely(usart->rx_fifo))
+ usart->reg->cr1 |= (1 << STM_USART_CR1_RXNEIE);
+ }
+#if HAS_SERIAL_SW_FLOW
+ /* If we've cleared RTS, check if there's space now and turn it back on */
+ if (usart->gpio_rts && usart->rts == 0 && ao_fifo_barely(usart->rx_fifo)) {
+ ao_gpio_set(usart->gpio_rts, usart->pin_rts, 0);
+ usart->rts = 1;
+ }
+#endif
+ c = u;
+ }
+ return c;
+}
+
+static char
+ao_usart_getchar(struct ao_stm_usart *usart)
+{
+ int c;
+ ao_arch_block_interrupts();
+ while ((c = _ao_usart_pollchar(usart)) == AO_READ_AGAIN)
+ ao_sleep(&usart->rx_fifo);
+ ao_arch_release_interrupts();
+ return (char) c;
+}
+
+static inline uint8_t
+_ao_usart_sleep_for(struct ao_stm_usart *usart, AO_TICK_TYPE timeout)
+{
+ return ao_sleep_for(&usart->rx_fifo, timeout);
+}
+
+static void
+ao_usart_putchar(struct ao_stm_usart *usart, char c)
+{
+ ao_arch_block_interrupts();
+ while (ao_fifo_full(usart->tx_fifo))
+ ao_sleep(&usart->tx_fifo);
+ ao_fifo_insert(usart->tx_fifo, c);
+ _ao_usart_tx_start(usart);
+ ao_arch_release_interrupts();
+}
+
+static void
+ao_usart_drain(struct ao_stm_usart *usart)
+{
+ ao_arch_block_interrupts();
+ while (!ao_fifo_empty(usart->tx_fifo) || usart->tx_running) {
+ usart->draining = 1;
+ ao_sleep(&usart->tx_fifo);
+ }
+ ao_arch_release_interrupts();
+}
+
+static const struct {
+ uint32_t baud;
+} ao_usart_speeds[] = {
+ [AO_SERIAL_SPEED_4800] = {
+ 4800
+ },
+ [AO_SERIAL_SPEED_9600] = {
+ 9600
+ },
+ [AO_SERIAL_SPEED_19200] = {
+ 19200
+ },
+ [AO_SERIAL_SPEED_57600] = {
+ 57600
+ },
+ [AO_SERIAL_SPEED_115200] = {
+ 115200
+ },
+};
+
+static void
+ao_usart_set_speed(struct ao_stm_usart *usart, uint8_t speed)
+{
+ uint32_t brr;
+
+ if (speed > AO_SERIAL_SPEED_115200)
+ return;
+#if HAS_SERIAL_1
+ if (usart == &ao_stm_usart1)
+ brr = AO_PCLK2 / ao_usart_speeds[speed].baud;
+ else
+#endif
+ brr = AO_PCLK1 / ao_usart_speeds[speed].baud;
+ usart->reg->brr = brr;
+}
+
+static void
+ao_usart_init(struct ao_stm_usart *usart, int hw_flow, uint8_t speed)
+{
+ usart->reg->cr1 = ((1 << STM_USART_CR1_UE) |
+ (0 << STM_USART_CR1_M) |
+ (0 << STM_USART_CR1_WAKE) |
+ (0 << STM_USART_CR1_PCE) |
+ (0 << STM_USART_CR1_PS) |
+ (0 << STM_USART_CR1_PEIE) |
+ (0 << STM_USART_CR1_TXEIE) |
+ (0 << STM_USART_CR1_TCIE) |
+ (1 << STM_USART_CR1_RXNEIE) |
+ (0 << STM_USART_CR1_IDLEIE) |
+ (1 << STM_USART_CR1_TE) |
+ (1 << STM_USART_CR1_RE) |
+ (0 << STM_USART_CR1_RWU) |
+ (0 << STM_USART_CR1_SBK));
+
+ usart->reg->cr2 = ((0 << STM_USART_CR2_LINEN) |
+ (STM_USART_CR2_STOP_1 << STM_USART_CR2_STOP) |
+ (0 << STM_USART_CR2_CLKEN) |
+ (0 << STM_USART_CR2_CPOL) |
+ (0 << STM_USART_CR2_CPHA) |
+ (0 << STM_USART_CR2_LBCL) |
+ (0 << STM_USART_CR2_LBDIE) |
+ (0 << STM_USART_CR2_LBDL) |
+ (0 << STM_USART_CR2_ADD));
+
+ usart->reg->cr3 = ((0 << STM_USART_CR3_CTSIE) |
+ (0 << STM_USART_CR3_CTSE) |
+ (0 << STM_USART_CR3_RTSE) |
+ (0 << STM_USART_CR3_DMAT) |
+ (0 << STM_USART_CR3_DMAR) |
+ (0 << STM_USART_CR3_SCEN) |
+ (0 << STM_USART_CR3_NACK) |
+ (0 << STM_USART_CR3_HDSEL) |
+ (0 << STM_USART_CR3_IRLP) |
+ (0 << STM_USART_CR3_IREN) |
+ (0 << STM_USART_CR3_EIE));
+
+ if (hw_flow)
+ usart->reg->cr3 |= ((1 << STM_USART_CR3_CTSE) |
+ (1 << STM_USART_CR3_RTSE));
+
+ ao_usart_set_speed(usart, speed);
+}
+
+#if HAS_SERIAL_1
+
+struct ao_stm_usart ao_stm_usart1;
+
+void stm_usart1_isr(void) { ao_usart_isr(&ao_stm_usart1, USE_SERIAL_1_STDIN); }
+
+char
+ao_serial1_getchar(void)
+{
+ return ao_usart_getchar(&ao_stm_usart1);
+}
+
+void
+ao_serial1_putchar(char c)
+{
+ ao_usart_putchar(&ao_stm_usart1, c);
+}
+
+int
+_ao_serial1_pollchar(void)
+{
+ return _ao_usart_pollchar(&ao_stm_usart1);
+}
+
+uint8_t
+_ao_serial1_sleep_for(AO_TICK_TYPE timeout)
+{
+ return _ao_usart_sleep_for(&ao_stm_usart1, timeout);
+}
+
+void
+ao_serial1_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart1);
+}
+
+void
+ao_serial1_set_speed(uint8_t speed)
+{
+ ao_usart_drain(&ao_stm_usart1);
+ ao_usart_set_speed(&ao_stm_usart1, speed);
+}
+#endif /* HAS_SERIAL_1 */
+
+#if HAS_SERIAL_2
+
+struct ao_stm_usart ao_stm_usart2;
+
+void stm_usart2_isr(void) { ao_usart_isr(&ao_stm_usart2, USE_SERIAL_2_STDIN); }
+
+char
+ao_serial2_getchar(void)
+{
+ return ao_usart_getchar(&ao_stm_usart2);
+}
+
+void
+ao_serial2_putchar(char c)
+{
+ ao_usart_putchar(&ao_stm_usart2, c);
+}
+
+int
+_ao_serial2_pollchar(void)
+{
+ return _ao_usart_pollchar(&ao_stm_usart2);
+}
+
+uint8_t
+_ao_serial2_sleep_for(AO_TICK_TYPE timeout)
+{
+ return _ao_usart_sleep_for(&ao_stm_usart2, timeout);
+}
+
+void
+ao_serial2_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart2);
+}
+
+void
+ao_serial2_set_speed(uint8_t speed)
+{
+ ao_usart_drain(&ao_stm_usart2);
+ ao_usart_set_speed(&ao_stm_usart2, speed);
+}
+
+#if HAS_SERIAL_SW_FLOW
+static void
+ao_serial2_cts(void)
+{
+ _ao_usart_cts(&ao_stm_usart2);
+}
+#endif
+
+#endif /* HAS_SERIAL_2 */
+
+#if HAS_SERIAL_3
+
+struct ao_stm_usart ao_stm_usart3;
+
+void stm_usart3_isr(void) { ao_usart_isr(&ao_stm_usart3, USE_SERIAL_3_STDIN); }
+
+char
+ao_serial3_getchar(void)
+{
+ return ao_usart_getchar(&ao_stm_usart3);
+}
+
+void
+ao_serial3_putchar(char c)
+{
+ ao_usart_putchar(&ao_stm_usart3, c);
+}
+
+int
+_ao_serial3_pollchar(void)
+{
+ return _ao_usart_pollchar(&ao_stm_usart3);
+}
+
+uint8_t
+_ao_serial3_sleep_for(AO_TICK_TYPE timeout)
+{
+ return _ao_usart_sleep_for(&ao_stm_usart3, timeout);
+}
+
+void
+ao_serial3_set_speed(uint8_t speed)
+{
+ ao_usart_drain(&ao_stm_usart3);
+ ao_usart_set_speed(&ao_stm_usart3, speed);
+}
+
+void
+ao_serial3_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart3);
+}
+#endif /* HAS_SERIAL_3 */
+
+#if HAS_SERIAL_SW_FLOW
+static void
+ao_serial_set_sw_rts_cts(struct ao_stm_usart *usart,
+ void (*isr)(void),
+ struct stm_gpio *port_rts,
+ uint8_t pin_rts,
+ struct stm_gpio *port_cts,
+ uint8_t pin_cts)
+{
+ /* Pull RTS low to note that there's space in the FIFO
+ */
+ ao_enable_output(port_rts, pin_rts, 0);
+ usart->gpio_rts = port_rts;
+ usart->pin_rts = pin_rts;
+ usart->rts = 1;
+
+ ao_exti_setup(port_cts, pin_cts, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, isr);
+ usart->gpio_cts = port_cts;
+ usart->pin_cts = pin_cts;
+}
+#endif
+
+void
+ao_serial_init(void)
+{
+#if HAS_SERIAL_1
+ /*
+ * TX RX
+ * PA9 PA10
+ * PB6 PB7 *
+ */
+
+#if SERIAL_1_PA9_PA10
+ ao_enable_port(&stm_gpioa);
+ stm_gpio_conf(&stm_gpioa, 9,
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+ stm_gpio_conf(&stm_gpioa, 10,
+ STM_GPIO_CR_MODE_INPUT,
+ STM_GPIO_CR_CNF_INPUT_FLOATING);
+
+ stm_set_afio_mapr(STM_AFIO_MAPR_USART1_REMAP,
+ STM_AFIO_MAPR_USART1_REMAP_PA9_PA10,
+ STM_AFIO_MAPR_USART1_REMAP_MASK);
+#else
+#if SERIAL_1_PB6_PB7
+ ao_enable_port(&stm_gpiob);
+ stm_gpio_conf(&stm_gpiob, 6,
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+ stm_gpio_conf(&stm_gpiob, 7,
+ STM_GPIO_CR_MODE_INPUT,
+ STM_GPIO_CR_CNF_INPUT_FLOATING);
+
+ stm_set_afio_mapr(STM_AFIO_MAPR_USART1_REMAP,
+ STM_AFIO_MAPR_USART1_REMAP_PB6_PB7,
+ STM_AFIO_MAPR_USART1_REMAP_MASK);
+#else
+#error "No SERIAL_1 port configuration specified"
+#endif
+#endif
+ /* Enable USART */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_USART1EN);
+
+ ao_stm_usart1.reg = &stm_usart1;
+ ao_usart_init(&ao_stm_usart1, 0);
+
+ stm_nvic_set_enable(STM_ISR_USART1_POS);
+ stm_nvic_set_priority(STM_ISR_USART1_POS, AO_STM_NVIC_MED_PRIORITY);
+#if USE_SERIAL_1_STDIN && !DELAY_SERIAL_1_STDIN
+ ao_add_stdio(_ao_serial1_pollchar,
+ ao_serial1_putchar,
+ NULL);
+#endif
+#endif
+
+#if HAS_SERIAL_2
+ /*
+ * TX RX
+ * PA2 PA3
+ * PD5 PD6
+ */
+
+#if SERIAL_2_PA2_PA3
+ ao_enable_port(&stm_gpioa);
+ stm_gpio_conf(&stm_gpioa, 2,
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+ stm_gpio_conf(&stm_gpioa, 3,
+ STM_GPIO_CR_MODE_INPUT,
+ STM_GPIO_CR_CNF_INPUT_FLOATING);
+
+#ifndef USE_SERIAL_2_FLOW
+#define USE_SERIAL_2_FLOW 0
+#define USE_SERIAL_2_SW_FLOW 0
+#endif
+
+# if USE_SERIAL_2_FLOW
+# if USE_SERIAL_2_SW_FLOW
+ ao_serial_set_sw_rts_cts(&ao_stm_usart2,
+ ao_serial2_cts,
+ SERIAL_2_PORT_RTS,
+ SERIAL_2_PIN_RTS,
+ SERIAL_2_PORT_CTS,
+ SERIAL_2_PIN_CTS);
+# else
+ stm_gpio_conf(&stm_gpioa, 0, /* CTS */
+ STM_GPIO_CR_MODE_INPUT,
+ STM_GPIO_CR_CNF_INPUT_FLOATING);
+ stm_gpio_conf(&stm_gpioa, 1, /* RTS */
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+# endif
+# endif
+ stm_set_afio_mapr(STM_AFIO_MAPR_USART2_REMAP,
+ STM_AFIO_MAPR_USART2_REMAP_PA0_PA1_PA2_PA3_PA4,
+ STM_AFIO_MAPR_USART2_REMAP_MASK);
+#elif SERIAL_2_PD5_PD6
+ ao_enable_port(&stm_gpiod);
+ stm_gpio_conf(&stm_gpiod, 5,
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+ stm_gpio_conf(&stm_gpiod, 6,
+ STM_GPIO_CR_MODE_INPUT,
+ STM_GPIO_CR_CNF_INPUT_FLOATING);
+
+# if USE_SERIAL_2_FLOW
+# if USE_SERIAL_2_SW_FLOW
+ ao_serial_set_sw_rts_cts(&ao_stm_usart2,
+ ao_serial2_cts,
+ SERIAL_2_PORT_RTS,
+ SERIAL_2_PIN_RTS,
+ SERIAL_2_PORT_CTS,
+ SERIAL_2_PIN_CTS);
+# else
+ stm_gpio_conf(&stm_gpiod, 3, /* CTS */
+ STM_GPIO_CR_MODE_INPUT,
+ STM_GPIO_CR_CNF_INPUT_FLOATING);
+ stm_gpio_conf(&stm_gpiod, 4, /* RTS */
+ STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+ STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+# endif
+# endif
+ stm_set_afio_mapr(STM_AFIO_MAPR_USART2_REMAP,
+ STM_AFIO_MAPR_USART2_REMAP_PD3_PD4_PD5_PD6_PD7,
+ STM_AFIO_MAPR_USART2_REMAP_MASK);
+#else
+#error "No SERIAL_2 port configuration specified"
+#endif
+ /* Enable USART */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART2EN);
+
+ ao_stm_usart2.reg = &stm_usart2;
+ ao_usart_init(&ao_stm_usart2, USE_SERIAL_2_FLOW && !USE_SERIAL_2_SW_FLOW, SERIAL_2_SPEED);
+
+ stm_nvic_set_enable(STM_ISR_USART2_POS);
+ stm_nvic_set_priority(STM_ISR_USART2_POS, AO_STM_NVIC_MED_PRIORITY);
+#if USE_SERIAL_2_STDIN && !DELAY_SERIAL_2_STDIN
+ ao_add_stdio(_ao_serial2_pollchar,
+ ao_serial2_putchar,
+ NULL);
+#endif
+#endif
+
+#if HAS_SERIAL_3
+ /*
+ * TX RX
+ * PB10 PB11
+ * PC10 PC11
+ * PD8 PD9
+ */
+#if SERIAL_3_PB10_PB11
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+
+ stm_afr_set(&stm_gpiob, 10, STM_AFR_AF7);
+ stm_afr_set(&stm_gpiob, 11, STM_AFR_AF7);
+#else
+#if SERIAL_3_PC10_PC11
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOCEN);
+
+ stm_afr_set(&stm_gpioc, 10, STM_AFR_AF7);
+ stm_afr_set(&stm_gpioc, 11, STM_AFR_AF7);
+#else
+#if SERIAL_3_PD8_PD9
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
+
+ stm_afr_set(&stm_gpiod, 8, STM_AFR_AF7);
+ stm_afr_set(&stm_gpiod, 9, STM_AFR_AF7);
+#else
+#error "No SERIAL_3 port configuration specified"
+#endif
+#endif
+#endif
+ /* Enable USART */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART3EN);
+
+ ao_stm_usart3.reg = &stm_usart3;
+ ao_usart_init(&ao_stm_usart3, 0);
+
+ stm_nvic_set_enable(STM_ISR_USART3_POS);
+ stm_nvic_set_priority(STM_ISR_USART3_POS, AO_STM_NVIC_MED_PRIORITY);
+#if USE_SERIAL_3_STDIN && !DELAY_SERIAL_3_STDIN
+ ao_add_stdio(_ao_serial3_pollchar,
+ ao_serial3_putchar,
+ NULL);
+#endif
+#endif
+}
--- /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; 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>
+
+struct ao_spi_stm_info {
+ uint8_t miso_dma_index;
+ uint8_t mosi_dma_index;
+ struct stm_spi *stm_spi;
+};
+
+static uint8_t ao_spi_mutex[STM_NUM_SPI];
+static uint8_t ao_spi_pin_config[STM_NUM_SPI];
+
+static const struct ao_spi_stm_info ao_spi_stm_info[STM_NUM_SPI] = {
+ {
+ .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_RX),
+ .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX),
+ &stm_spi1
+ },
+ {
+ .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_RX),
+ .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_TX),
+ &stm_spi2
+ }
+};
+
+static uint8_t spi_dev_null;
+
+#if DEBUG
+static struct {
+ uint8_t task;
+ uint8_t which;
+ AO_TICK_TYPE tick;
+ uint16_t len;
+} spi_tasks[64];
+static uint8_t spi_task_index;
+
+static void
+validate_spi(struct stm_spi *stm_spi, int which, uint16_t len)
+{
+ uint32_t sr = stm_spi->sr;
+
+ if (stm_spi != &stm_spi2)
+ return;
+ spi_tasks[spi_task_index].task = ao_cur_task ? ao_cur_task->task_id : 0;
+ spi_tasks[spi_task_index].which = which;
+ spi_tasks[spi_task_index].tick = ao_time();
+ spi_tasks[spi_task_index].len = len;
+ spi_task_index = (spi_task_index + 1) & (63);
+ if (sr & (1 << STM_SPI_SR_FRE))
+ ao_panic(0x40 | 1);
+ if (sr & (1 << STM_SPI_SR_BSY))
+ ao_panic(0x40 | 2);
+ if (sr & (1 << STM_SPI_SR_OVR))
+ ao_panic(0x40 | 3);
+ if (sr & (1 << STM_SPI_SR_MODF))
+ ao_panic(0x40 | 4);
+ if (sr & (1 << STM_SPI_SR_UDR))
+ ao_panic(0x40 | 5);
+ if ((sr & (1 << STM_SPI_SR_TXE)) == 0)
+ ao_panic(0x40 | 6);
+ if (sr & (1 << STM_SPI_SR_RXNE))
+ ao_panic(0x40 | 7);
+ if (which != 5 && which != 6 && which != 13)
+ if (ao_cur_task->task_id != ao_spi_mutex[1])
+ ao_panic(0x40 | 8);
+}
+#else
+#define validate_spi(stm_spi, which, len) do { (void) (which); (void) (len); } while (0)
+#endif
+
+static void
+ao_spi_set_dma_mosi(uint8_t id, const void *data, uint16_t len, uint32_t minc)
+{
+ struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+ uint8_t mosi_dma_index = ao_spi_stm_info[id].mosi_dma_index;
+
+ ao_dma_set_transfer(mosi_dma_index,
+ &stm_spi->dr,
+ (void *) data,
+ len,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+ (minc << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+}
+
+static void
+ao_spi_set_dma_miso(uint8_t id, void *data, uint16_t len, uint32_t minc)
+{
+ struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+ uint8_t miso_dma_index = ao_spi_stm_info[id].miso_dma_index;
+
+ ao_dma_set_transfer(miso_dma_index,
+ &stm_spi->dr,
+ data,
+ len,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_VERY_HIGH << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+ (minc << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+}
+
+static void
+ao_spi_run(uint8_t id, uint8_t which, uint16_t len)
+{
+ struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+ uint8_t mosi_dma_index = ao_spi_stm_info[id].mosi_dma_index;
+ uint8_t miso_dma_index = ao_spi_stm_info[id].miso_dma_index;
+
+ validate_spi(stm_spi, which, len);
+
+ stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+ (0 << STM_SPI_CR2_RXNEIE) |
+ (0 << STM_SPI_CR2_ERRIE) |
+ (0 << STM_SPI_CR2_SSOE) |
+ (1 << STM_SPI_CR2_TXDMAEN) |
+ (1 << STM_SPI_CR2_RXDMAEN));
+
+ ao_dma_start(miso_dma_index);
+ ao_dma_start(mosi_dma_index);
+
+ ao_arch_critical(
+ while (!ao_dma_done[miso_dma_index])
+ ao_sleep(&ao_dma_done[miso_dma_index]);
+ );
+
+ while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0);
+ while (stm_spi->sr & (1 << STM_SPI_SR_BSY));
+
+ validate_spi(stm_spi, which+1, len);
+
+ stm_spi->cr2 = 0;
+
+ ao_dma_done_transfer(mosi_dma_index);
+ ao_dma_done_transfer(miso_dma_index);
+}
+
+void
+ao_spi_send(const void *block, uint16_t len, uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+
+ /* Set up the transmit DMA to deliver data */
+ ao_spi_set_dma_mosi(id, block, len, 1);
+
+ /* Set up the receive DMA -- when this is done, we know the SPI unit
+ * is idle. Without this, we'd have to poll waiting for the BSY bit to
+ * be cleared
+ */
+ ao_spi_set_dma_miso(id, &spi_dev_null, len, 0);
+
+ ao_spi_run(id, 1, len);
+}
+
+void
+ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+
+ /* Set up the transmit DMA to deliver data */
+ ao_spi_set_dma_mosi(id, &value, len, 0);
+
+ /* Set up the receive DMA -- when this is done, we know the SPI unit
+ * is idle. Without this, we'd have to poll waiting for the BSY bit to
+ * be cleared
+ */
+ ao_spi_set_dma_miso(id, &spi_dev_null, len, 0);
+
+ ao_spi_run(id, 3, len);
+}
+
+void
+ao_spi_start_bytes(uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+ struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+ stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+ (0 << STM_SPI_CR2_RXNEIE) |
+ (0 << STM_SPI_CR2_ERRIE) |
+ (0 << STM_SPI_CR2_SSOE) |
+ (0 << STM_SPI_CR2_TXDMAEN) |
+ (0 << STM_SPI_CR2_RXDMAEN));
+ validate_spi(stm_spi, 5, 0xffff);
+}
+
+void
+ao_spi_stop_bytes(uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+ struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+ while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0)
+ ;
+ while (stm_spi->sr & (1 << STM_SPI_SR_BSY))
+ ;
+ /* Clear the OVR flag */
+ (void) stm_spi->dr;
+ (void) stm_spi->sr;
+ validate_spi(stm_spi, 6, 0xffff);
+ stm_spi->cr2 = 0;
+}
+
+void
+ao_spi_send_sync(const void *block, uint16_t len, uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+ const uint8_t *b = block;
+ struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+ stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+ (0 << STM_SPI_CR2_RXNEIE) |
+ (0 << STM_SPI_CR2_ERRIE) |
+ (0 << STM_SPI_CR2_SSOE) |
+ (0 << STM_SPI_CR2_TXDMAEN) |
+ (0 << STM_SPI_CR2_RXDMAEN));
+ validate_spi(stm_spi, 7, len);
+ while (len--) {
+ while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE)));
+ stm_spi->dr = *b++;
+ }
+ while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0)
+ ;
+ while (stm_spi->sr & (1 << STM_SPI_SR_BSY))
+ ;
+ /* Clear the OVR flag */
+ (void) stm_spi->dr;
+ (void) stm_spi->sr;
+ validate_spi(stm_spi, 8, len);
+}
+
+void
+ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+
+ spi_dev_null = 0xff;
+
+ /* Set up transmit DMA to make the SPI hardware actually run */
+ ao_spi_set_dma_mosi(id, &spi_dev_null, len, 0);
+
+ /* Set up the receive DMA to capture data */
+ ao_spi_set_dma_miso(id, block, len, 1);
+
+ ao_spi_run(id, 9, len);
+}
+
+void
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+
+ /* Set up transmit DMA to send data */
+ ao_spi_set_dma_mosi(id, out, len, 1);
+
+ /* Set up the receive DMA to capture data */
+ ao_spi_set_dma_miso(id, in, len, 1);
+
+ ao_spi_run(id, 11, len);
+}
+
+#define stm_spi_input_disable(gpio, pin) do { \
+ stm_gpio_conf(gpio, pin, \
+ STM_GPIO_CR_MODE_INPUT, \
+ STM_GPIO_CR_CNF_INPUT_FLOATING); \
+ } while(0)
+
+#define stm_spi_output_disable(gpio, pin, mode) do { \
+ ao_gpio_set(gpio, pin, 1); \
+ stm_gpio_conf(gpio, pin, \
+ mode, \
+ STM_GPIO_CR_CNF_OUTPUT_PUSH_PULL); \
+ } while(0)
+
+static void
+ao_spi_disable_pin_config(uint8_t spi_pin_config)
+{
+ /* Disable current config
+ */
+ switch (spi_pin_config) {
+#if SPI_1_PA5_PA6_PA7
+ case AO_SPI_1_PA5_PA6_PA7:
+ stm_spi_output_disable(&stm_gpioa, 5, SPI_1_MODE_OUTPUT);
+#ifndef SPI_1_PA6_DISABLE
+ stm_spi_input_disable(&stm_gpioa, 6);
+#endif
+ stm_spi_output_disable(&stm_gpioa, 7, SPI_1_MODE_OUTPUT);
+ break;
+#endif
+#if SPI_1_PB3_PB4_PB5
+ case AO_SPI_1_PB3_PB4_PB5:
+ stm_spi_output_disable(&stm_gpiob, 3, SPI_1_MODE_OUTPUT);
+ stm_spi_input_disable(&stm_gpiob, 4);
+ stm_spi_output_disable(&stm_gpiob, 5, SPI_1_MODE_OUTPUT);
+ break;
+#endif
+#if SPI_2_PB13_PB14_PB15
+ case AO_SPI_2_PB13_PB14_PB15:
+ stm_spi_output_disable(&stm_gpiob, 13, SPI_2_MODE_OUTPUT);
+ stm_spi_input_disable(&stm_gpiob, 14);
+ stm_spi_output_disable(&stm_gpiob, 15, SPI_2_MODE_OUTPUT);
+ break;
+#endif
+ }
+}
+
+#define stm_spi_input_enable(gpio, pin) do { \
+ stm_gpio_conf(gpio, pin, \
+ STM_GPIO_CR_MODE_INPUT, \
+ STM_GPIO_CR_CNF_INPUT_FLOATING); \
+ } while(0)
+
+#define stm_spi_output_enable(gpio, pin, mode) do { \
+ stm_gpio_conf(gpio, pin, \
+ mode, \
+ STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL); \
+ } while(0)
+
+static void
+ao_spi_enable_pin_config(uint8_t spi_pin_config)
+{
+ /* Enable new config
+ */
+ switch (spi_pin_config) {
+#if SPI_1_PA5_PA6_PA7
+ case AO_SPI_1_PA5_PA6_PA7:
+ stm_set_afio_mapr(STM_AFIO_MAPR_SPI1_REMAP,
+ STM_AFIO_MAPR_SPI1_REMAP_PA4_PA5_PA6_PA7,
+ STM_AFIO_MAPR_SPI1_REMAP_MASK);
+ stm_spi_output_enable(&stm_gpioa, 5, SPI_1_MODE_OUTPUT);
+#ifndef SPI_1_PA6_DISABLE
+ stm_spi_input_enable(&stm_gpioa, 6);
+#endif
+ stm_spi_output_enable(&stm_gpioa, 7, SPI_1_MODE_OUTPUT);
+ break;
+#endif
+#if SPI_1_PB3_PB4_PB5
+ case AO_SPI_1_PB3_PB4_PB5:
+ stm_set_afio_mapr(STM_AFIO_MAPR_SPI1_REMAP,
+ STM_AFIO_MAPR_SPI1_REMAP_PA15_PB3_PB4_PB5,
+ STM_AFIO_MAPR_SPI1_REMAP_MASK);
+ stm_spi_output_enable(&stm_gpiob, 3, SPI_1_MODE_OUTPUT);
+ stm_spi_input_enable(&stm_gpiob, 4);
+ stm_spi_output_enable(&stm_gpiob, 5, SPI_1_MODE_OUTPUT);
+ break;
+#endif
+#if SPI_2_PB13_PB14_PB15
+ case AO_SPI_2_PB13_PB14_PB15:
+ stm_spi_output_enable(&stm_gpiob, 13, SPI_2_MODE_OUTPUT);
+ stm_spi_input_enable(&stm_gpiob, 14);
+ stm_spi_output_enable(&stm_gpiob, 15, SPI_2_MODE_OUTPUT);
+ break;
+#endif
+ }
+}
+
+static void
+ao_spi_config(uint8_t spi_index, uint32_t speed)
+{
+ uint8_t spi_pin_config = AO_SPI_PIN_CONFIG(spi_index);
+ uint8_t id = AO_SPI_INDEX(spi_index);
+ struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+ if (spi_pin_config != ao_spi_pin_config[id]) {
+
+ /* Disable old config
+ */
+ ao_spi_disable_pin_config(ao_spi_pin_config[id]);
+
+ /* Enable new config
+ */
+ ao_spi_enable_pin_config(spi_pin_config);
+
+ /* Remember current config
+ */
+ ao_spi_pin_config[id] = spi_pin_config;
+ }
+
+ /* Turn the SPI transceiver on and set the mode */
+ stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) | /* Three wire mode */
+ (0 << STM_SPI_CR1_BIDIOE) |
+ (0 << STM_SPI_CR1_CRCEN) | /* CRC disabled */
+ (0 << STM_SPI_CR1_CRCNEXT) |
+ (0 << STM_SPI_CR1_DFF) |
+ (0 << STM_SPI_CR1_RXONLY) |
+ (1 << STM_SPI_CR1_SSM) | /* Software SS handling */
+ (1 << STM_SPI_CR1_SSI) | /* ... */
+ (0 << STM_SPI_CR1_LSBFIRST) | /* Big endian */
+ (1 << STM_SPI_CR1_SPE) | /* Enable SPI unit */
+ (speed << STM_SPI_CR1_BR) | /* baud rate to pclk/4 */
+ (1 << STM_SPI_CR1_MSTR) |
+ (AO_SPI_CPOL(spi_index) << STM_SPI_CR1_CPOL) | /* Format */
+ (AO_SPI_CPHA(spi_index) << STM_SPI_CR1_CPHA));
+ validate_spi(stm_spi, 13, 0);
+}
+
+uint8_t
+ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+
+ if (!ao_mutex_try(&ao_spi_mutex[id], task_id))
+ return 0;
+ ao_spi_config(spi_index, speed);
+ return 1;
+}
+
+void
+ao_spi_get(uint8_t spi_index, uint32_t speed)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+
+ ao_mutex_get(&ao_spi_mutex[id]);
+ ao_spi_config(spi_index, speed);
+}
+
+void
+ao_spi_put(uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+ struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+ stm_spi->cr1 = 0;
+ 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)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+ struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+ ao_spi_disable_pin_config(AO_SPI_PIN_CONFIG(spi_index));
+
+ stm_spi->cr1 = 0;
+ stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+ (0 << STM_SPI_CR2_RXNEIE) |
+ (0 << STM_SPI_CR2_ERRIE) |
+ (0 << STM_SPI_CR2_SSOE) |
+ (0 << STM_SPI_CR2_TXDMAEN) |
+ (0 << STM_SPI_CR2_RXDMAEN));
+
+ /* Clear any pending data and error flags */
+ (void) stm_spi->dr;
+ (void) stm_spi->sr;
+}
+
+#if DEBUG
+void
+ao_spi_dump_cmd(void)
+{
+ int s;
+
+ for (s = 0; s < 64; s++) {
+ int i = (spi_task_index + s) & 63;
+ if (spi_tasks[i].which) {
+ int t;
+ const char *name = "(none)";
+ for (t = 0; t < ao_num_tasks; t++)
+ if (ao_tasks[t]->task_id == spi_tasks[i].task) {
+ name = ao_tasks[t]->name;
+ break;
+ }
+ printf("%2d: %5d task %2d which %2d len %5d %s\n",
+ s,
+ spi_tasks[i].tick,
+ spi_tasks[i].task,
+ spi_tasks[i].which,
+ spi_tasks[i].len,
+ name);
+ }
+ }
+ for (s = 0; s < STM_NUM_SPI; s++) {
+ struct stm_spi *spi = ao_spi_stm_info[s].stm_spi;
+
+ printf("%1d: mutex %2d index %3d miso dma %3d mosi dma %3d",
+ s, ao_spi_mutex[s], ao_spi_index[s],
+ ao_spi_stm_info[s].miso_dma_index,
+ ao_spi_stm_info[s].mosi_dma_index);
+ printf(" cr1 %04x cr2 %02x sr %03x\n",
+ spi->cr1, spi->cr2, spi->sr);
+ }
+
+}
+
+static const struct ao_cmds ao_spi_cmds[] = {
+ { ao_spi_dump_cmd, "S\0Dump SPI status" },
+ { 0, NULL }
+};
+#endif
+
+void
+ao_spi_init(void)
+{
+#if HAS_SPI_1
+# if SPI_1_PA5_PA6_PA7
+ ao_enable_port(&stm_gpioa);
+# endif
+# if SPI_1_PB3_PB4_PB5
+ ao_enable_port(&stm_gpiob);
+# endif
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
+ ao_spi_pin_config[0] = AO_SPI_CONFIG_NONE;
+ ao_spi_channel_init(0);
+#endif
+
+#if HAS_SPI_2
+# if SPI_2_PB13_PB14_PB15
+ ao_enable_port(&stm_gpiob);
+# endif
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
+ ao_spi_pin_config[1] = AO_SPI_CONFIG_NONE;
+ ao_spi_channel_init(1);
+#endif
+#if DEBUG
+ ao_cmd_register(&ao_spi_cmds[0]);
+#endif
+}
--- /dev/null
+/*
+ * Copyright © 2023 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_task.h>
+
+#ifndef HAS_TICK
+#define HAS_TICK 1
+#endif
+
+volatile AO_TICK_TYPE ao_tick_count;
+
+AO_TICK_TYPE
+ao_time(void)
+{
+ return ao_tick_count;
+}
+
+uint64_t
+ao_time_ns(void)
+{
+ AO_TICK_TYPE before, after;
+ uint32_t val;
+
+ do {
+ before = ao_tick_count;
+ val = stm_systick.val;
+ after = ao_tick_count;
+ } while (before != after);
+
+ return (uint64_t) after * (1000000000ULL / AO_HERTZ) +
+ (uint64_t) val * (1000000000ULL / AO_SYSTICK);
+}
+
+#if AO_DATA_ALL
+volatile uint8_t ao_data_interval = 1;
+volatile uint8_t ao_data_count;
+#endif
+
+void stm_systick_isr(void)
+{
+ if (stm_systick.ctrl & (1 << STM_SYSTICK_CTRL_COUNTFLAG)) {
+ ++ao_tick_count;
+ ao_task_check_alarm();
+#if AO_DATA_ALL
+ if (++ao_data_count == ao_data_interval && ao_data_interval) {
+ ao_data_count = 0;
+#if HAS_FAKE_FLIGHT
+ if (ao_fake_flight_active)
+ ao_fake_flight_poll();
+ else
+#endif
+ ao_adc_poll();
+#if (AO_DATA_ALL & ~(AO_DATA_ADC))
+ ao_wakeup((void *) &ao_data_count);
+#endif
+ }
+#endif
+#ifdef AO_TIMER_HOOK
+ AO_TIMER_HOOK;
+#endif
+ }
+}
+
+#if HAS_ADC
+void
+ao_timer_set_adc_interval(uint8_t interval)
+{
+ ao_arch_critical(
+ ao_data_interval = interval;
+ ao_data_count = 0;
+ );
+}
+#endif
+
+#define SYSTICK_RELOAD (AO_SYSTICK / 100 - 1)
+
+void
+ao_timer_init(void)
+{
+ stm_systick.load = SYSTICK_RELOAD;
+ stm_systick.val = 0;
+ stm_systick.ctrl = ((1 << STM_SYSTICK_CTRL_ENABLE) |
+ (1 << STM_SYSTICK_CTRL_TICKINT) |
+ (STM_SYSTICK_CTRL_CLKSOURCE_HCLK_8 << STM_SYSTICK_CTRL_CLKSOURCE));
+ stm_scb.shpr3 |= (uint32_t) AO_STM_NVIC_CLOCK_PRIORITY << 24;
+}
--- /dev/null
+/*
+ * Copyright © 2023 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_usb.h"
+#include "ao_product.h"
+
+#define USB_DEBUG 0
+#define USB_DEBUG_DATA 0
+#define USB_ECHO 0
+
+#ifndef USE_USB_STDIO
+#define USE_USB_STDIO 1
+#endif
+
+#if USE_USB_STDIO
+#define AO_USB_OUT_SLEEP_ADDR (&ao_stdin_ready)
+#else
+#define AO_USB_OUT_SLEEP_ADDR (&ao_usb_out_avail)
+#endif
+
+#if USB_DEBUG
+#define debug(format, args...) printf(format, ## args);
+#else
+#define debug(format, args...)
+#endif
+
+#if USB_DEBUG_DATA
+#define debug_data(format, args...) printf(format, ## args);
+#else
+#define debug_data(format, args...)
+#endif
+
+struct ao_usb_setup {
+ uint8_t dir_type_recip;
+ uint8_t request;
+ uint16_t value;
+ uint16_t index;
+ uint16_t length;
+} ao_usb_setup;
+
+static uint8_t ao_usb_ep0_state;
+
+/* Pending EP0 IN data */
+static const uint8_t *ao_usb_ep0_in_data; /* Remaining data */
+static uint8_t ao_usb_ep0_in_len; /* Remaining amount */
+
+/* Temp buffer for smaller EP0 in data */
+static uint8_t ao_usb_ep0_in_buf[2];
+
+/* Pending EP0 OUT data */
+static uint8_t *ao_usb_ep0_out_data;
+static uint8_t ao_usb_ep0_out_len;
+
+/*
+ * Objects allocated in special USB memory
+ */
+
+/* Buffer description tables */
+static union stm_usb_bdt *ao_usb_bdt;
+/* USB address of end of allocated storage */
+static uint16_t ao_usb_sram_addr;
+
+/* Pointer to ep0 tx/rx buffers in USB memory */
+static uint32_t *ao_usb_ep0_tx_buffer;
+static uint32_t *ao_usb_ep0_rx_buffer;
+
+/* Pointer to bulk data tx/rx buffers in USB memory */
+static uint32_t *ao_usb_in_tx_buffer;
+static uint32_t *ao_usb_out_rx_buffer;
+
+/* System ram shadow of USB buffer; writing individual bytes is
+ * too much of a pain (sigh) */
+static uint8_t ao_usb_tx_buffer[AO_USB_IN_SIZE];
+static uint8_t ao_usb_tx_count;
+
+static uint8_t ao_usb_rx_buffer[AO_USB_OUT_SIZE];
+static uint8_t ao_usb_rx_count, ao_usb_rx_pos;
+
+/*
+ * End point register indices
+ */
+
+#define AO_USB_CONTROL_EPR 0
+#define AO_USB_INT_EPR 1
+#define AO_USB_OUT_EPR 2
+#define AO_USB_IN_EPR 3
+
+/* Marks when we don't need to send an IN packet.
+ * This happens only when the last IN packet is not full,
+ * otherwise the host will expect to keep seeing packets.
+ * Send a zero-length packet as required
+ */
+static uint8_t ao_usb_in_flushed;
+
+/* Marks when we have delivered an IN packet to the hardware
+ * and it has not been received yet. ao_sleep on this address
+ * to wait for it to be delivered.
+ */
+static uint8_t ao_usb_in_pending;
+
+/* Marks when an OUT packet has been received by the hardware
+ * but not pulled to the shadow buffer.
+ */
+static uint8_t ao_usb_out_avail;
+uint8_t ao_usb_running;
+static uint8_t ao_usb_configuration;
+
+#define AO_USB_EP0_GOT_RESET 1
+#define AO_USB_EP0_GOT_SETUP 2
+#define AO_USB_EP0_GOT_RX_DATA 4
+#define AO_USB_EP0_GOT_TX_ACK 8
+
+static uint8_t ao_usb_ep0_receive;
+static uint8_t ao_usb_address;
+static uint8_t ao_usb_address_pending;
+
+static inline uint32_t set_toggle(uint32_t current_value,
+ uint32_t mask,
+ uint32_t desired_value)
+{
+ return (current_value ^ desired_value) & mask;
+}
+
+static inline uint32_t *ao_usb_packet_buffer_addr(uint16_t sram_addr)
+{
+ return (uint32_t *) (((void *) ((uint8_t *) stm_usb_sram + 2 * sram_addr)));
+}
+
+static inline uint32_t ao_usb_epr_stat_rx(uint32_t epr) {
+ return (epr >> STM_USB_EPR_STAT_RX) & STM_USB_EPR_STAT_RX_MASK;
+}
+
+static inline uint32_t ao_usb_epr_stat_tx(uint32_t epr) {
+ return (epr >> STM_USB_EPR_STAT_TX) & STM_USB_EPR_STAT_TX_MASK;
+}
+
+static inline uint32_t ao_usb_epr_ctr_rx(uint32_t epr) {
+ return (epr >> STM_USB_EPR_CTR_RX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_ctr_tx(uint32_t epr) {
+ return (epr >> STM_USB_EPR_CTR_TX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_setup(uint32_t epr) {
+ return (epr >> STM_USB_EPR_SETUP) & 1;
+}
+
+static inline uint32_t ao_usb_epr_dtog_rx(uint32_t epr) {
+ return (epr >> STM_USB_EPR_DTOG_RX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_dtog_tx(uint32_t epr) {
+ return (epr >> STM_USB_EPR_DTOG_TX) & 1;
+}
+
+/*
+ * Set current device address and mark the
+ * interface as active
+ */
+static void
+ao_usb_set_address(uint8_t address)
+{
+ debug("ao_usb_set_address %02x\n", address);
+ stm_usb.daddr = (1 << STM_USB_DADDR_EF) | address;
+ ao_usb_address_pending = 0;
+}
+
+/*
+ * Write these values to preserve register contents under HW changes
+ */
+
+#define STM_USB_EPR_INVARIANT ((1 << STM_USB_EPR_CTR_RX) | \
+ (STM_USB_EPR_DTOG_RX_WRITE_INVARIANT << STM_USB_EPR_DTOG_RX) | \
+ (STM_USB_EPR_STAT_RX_WRITE_INVARIANT << STM_USB_EPR_STAT_RX) | \
+ (1 << STM_USB_EPR_CTR_TX) | \
+ (STM_USB_EPR_DTOG_TX_WRITE_INVARIANT << STM_USB_EPR_DTOG_TX) | \
+ (STM_USB_EPR_STAT_TX_WRITE_INVARIANT << STM_USB_EPR_STAT_TX))
+
+#define STM_USB_EPR_INVARIANT_MASK ((1 << STM_USB_EPR_CTR_RX) | \
+ (STM_USB_EPR_DTOG_RX_MASK << STM_USB_EPR_DTOG_RX) | \
+ (STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX) | \
+ (1 << STM_USB_EPR_CTR_TX) | \
+ (STM_USB_EPR_DTOG_TX_MASK << STM_USB_EPR_DTOG_TX) | \
+ (STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX))
+
+/*
+ * These bits are purely under sw control, so preserve them in the
+ * register by re-writing what was read
+ */
+#define STM_USB_EPR_PRESERVE_MASK ((STM_USB_EPR_EP_TYPE_MASK << STM_USB_EPR_EP_TYPE) | \
+ (1 << STM_USB_EPR_EP_KIND) | \
+ (STM_USB_EPR_EA_MASK << STM_USB_EPR_EA))
+
+#define TX_DBG 0
+#define RX_DBG 0
+
+#if TX_DBG
+#define _tx_dbg0(msg) _dbg(__LINE__,msg,0)
+#define _tx_dbg1(msg,value) _dbg(__LINE__,msg,value)
+#else
+#define _tx_dbg0(msg)
+#define _tx_dbg1(msg,value)
+#endif
+
+#if RX_DBG
+#define _rx_dbg0(msg) _dbg(__LINE__,msg,0)
+#define _rx_dbg1(msg,value) _dbg(__LINE__,msg,value)
+#else
+#define _rx_dbg0(msg)
+#define _rx_dbg1(msg,value)
+#endif
+
+#if TX_DBG || RX_DBG
+static void _dbg(int line, char *msg, uint32_t value);
+#endif
+
+/*
+ * Set the state of the specified endpoint register to a new
+ * value. This is tricky because the bits toggle where the new
+ * value is one, and we need to write invariant values in other
+ * spots of the register. This hardware is strange...
+ */
+static void
+_ao_usb_set_stat_tx(int ep, uint32_t stat_tx)
+{
+ uint32_t epr_write, epr_old;
+
+ _tx_dbg1("set_stat_tx top", stat_tx);
+ epr_old = epr_write = stm_usb.epr[ep];
+ epr_write &= STM_USB_EPR_PRESERVE_MASK;
+ epr_write |= STM_USB_EPR_INVARIANT;
+ epr_write |= set_toggle(epr_old,
+ STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX,
+ stat_tx << STM_USB_EPR_STAT_TX);
+ stm_usb.epr[ep] = epr_write;
+ _tx_dbg1("set_stat_tx bottom", epr_write);
+}
+
+static void
+ao_usb_set_stat_tx(int ep, uint32_t stat_tx)
+{
+ ao_arch_block_interrupts();
+ _ao_usb_set_stat_tx(ep, stat_tx);
+ ao_arch_release_interrupts();
+}
+
+static void
+_ao_usb_set_stat_rx(int ep, uint32_t stat_rx) {
+ uint32_t epr_write, epr_old;
+
+ epr_write = epr_old = stm_usb.epr[ep];
+ epr_write &= STM_USB_EPR_PRESERVE_MASK;
+ epr_write |= STM_USB_EPR_INVARIANT;
+ epr_write |= set_toggle(epr_old,
+ STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX,
+ stat_rx << STM_USB_EPR_STAT_RX);
+ stm_usb.epr[ep] = epr_write;
+}
+
+static void
+ao_usb_set_stat_rx(int ep, uint32_t stat_rx) {
+ ao_arch_block_interrupts();
+ _ao_usb_set_stat_rx(ep, stat_rx);
+ ao_arch_release_interrupts();
+}
+
+/*
+ * Set just endpoint 0, for use during startup
+ */
+
+static void
+ao_usb_init_ep(uint8_t ep, uint32_t addr, uint32_t type, uint32_t stat_rx, uint32_t stat_tx)
+{
+ uint32_t epr;
+ ao_arch_block_interrupts();
+ epr = stm_usb.epr[ep];
+ epr = ((0 << STM_USB_EPR_CTR_RX) |
+ (epr & (1 << STM_USB_EPR_DTOG_RX)) |
+ set_toggle(epr,
+ (STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX),
+ (stat_rx << STM_USB_EPR_STAT_RX)) |
+ (type << STM_USB_EPR_EP_TYPE) |
+ (0 << STM_USB_EPR_EP_KIND) |
+ (0 << STM_USB_EPR_CTR_TX) |
+ (epr & (1 << STM_USB_EPR_DTOG_TX)) |
+ set_toggle(epr,
+ (STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX),
+ (stat_tx << STM_USB_EPR_STAT_TX)) |
+ (addr << STM_USB_EPR_EA));
+ stm_usb.epr[ep] = epr;
+ ao_arch_release_interrupts();
+ debug ("writing epr[%d] 0x%08x wrote 0x%08x\n",
+ ep, epr, stm_usb.epr[ep]);
+}
+
+static void
+ao_usb_set_ep0(void)
+{
+ uint8_t e;
+
+ ao_usb_sram_addr = 0;
+
+ /* buffer table is at the start of USB memory */
+ stm_usb.btable = 0;
+ ao_usb_bdt = (void *) stm_usb_sram;
+
+ ao_usb_sram_addr += 8 * STM_USB_BDT_SIZE;
+
+ /* Set up EP 0 - a Control end point with 32 bytes of in and out buffers */
+
+ ao_usb_bdt[0].single.addr_tx = ao_usb_sram_addr;
+ ao_usb_bdt[0].single.count_tx = 0;
+ ao_usb_ep0_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_sram_addr += AO_USB_CONTROL_SIZE;
+
+ ao_usb_bdt[0].single.addr_rx = ao_usb_sram_addr;
+ ao_usb_bdt[0].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
+ (((AO_USB_CONTROL_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
+ ao_usb_ep0_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_sram_addr += AO_USB_CONTROL_SIZE;
+
+ ao_usb_init_ep(AO_USB_CONTROL_EPR, AO_USB_CONTROL_EP,
+ STM_USB_EPR_EP_TYPE_CONTROL,
+ STM_USB_EPR_STAT_RX_VALID,
+ STM_USB_EPR_STAT_TX_NAK);
+
+ /* Clear all of the other endpoints */
+ for (e = 1; e < 8; e++) {
+ ao_usb_init_ep(e, 0,
+ STM_USB_EPR_EP_TYPE_CONTROL,
+ STM_USB_EPR_STAT_RX_DISABLED,
+ STM_USB_EPR_STAT_TX_DISABLED);
+ }
+
+ ao_usb_set_address(0);
+
+ ao_usb_running = 0;
+
+ /* Reset our internal state
+ */
+
+ ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+ ao_usb_ep0_in_data = NULL;
+ ao_usb_ep0_in_len = 0;
+
+ ao_usb_ep0_out_data = 0;
+ ao_usb_ep0_out_len = 0;
+}
+
+static void
+ao_usb_set_configuration(void)
+{
+ debug ("ao_usb_set_configuration\n");
+
+ /* Set up the INT end point */
+ ao_usb_bdt[AO_USB_INT_EPR].single.addr_tx = ao_usb_sram_addr;
+ ao_usb_bdt[AO_USB_INT_EPR].single.count_tx = 0;
+ ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_sram_addr += AO_USB_INT_SIZE;
+
+ ao_usb_init_ep(AO_USB_INT_EPR,
+ AO_USB_INT_EP,
+ STM_USB_EPR_EP_TYPE_INTERRUPT,
+ STM_USB_EPR_STAT_RX_DISABLED,
+ STM_USB_EPR_STAT_TX_NAK);
+
+ /* Set up the OUT end point */
+ ao_usb_bdt[AO_USB_OUT_EPR].single.addr_rx = ao_usb_sram_addr;
+ ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
+ (((AO_USB_OUT_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
+ ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_sram_addr += AO_USB_OUT_SIZE;
+
+ ao_usb_init_ep(AO_USB_OUT_EPR,
+ AO_USB_OUT_EP,
+ STM_USB_EPR_EP_TYPE_BULK,
+ STM_USB_EPR_STAT_RX_VALID,
+ STM_USB_EPR_STAT_TX_DISABLED);
+
+ /* Set up the IN end point */
+ ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_sram_addr;
+ ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = 0;
+ ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_sram_addr += AO_USB_IN_SIZE;
+
+ ao_usb_init_ep(AO_USB_IN_EPR,
+ AO_USB_IN_EP,
+ STM_USB_EPR_EP_TYPE_BULK,
+ STM_USB_EPR_STAT_RX_DISABLED,
+ STM_USB_EPR_STAT_TX_NAK);
+
+ ao_usb_in_flushed = 0;
+ ao_usb_in_pending = 0;
+ ao_wakeup(&ao_usb_in_pending);
+
+ ao_usb_out_avail = 0;
+ ao_usb_configuration = 0;
+
+ ao_usb_running = 1;
+ ao_wakeup(&ao_usb_running);
+}
+
+static uint16_t control_count;
+static uint16_t int_count;
+static uint16_t in_count;
+static uint16_t out_count;
+static uint16_t reset_count;
+
+/* The USB memory holds 16 bit values on 32 bit boundaries
+ * and must be accessed only in 32 bit units. Sigh.
+ */
+
+static inline void
+ao_usb_write_byte(uint8_t byte, uint32_t *base, uint16_t offset)
+{
+ base += offset >> 1;
+ if (offset & 1) {
+ *base = (*base & 0xff) | ((uint32_t) byte << 8);
+ } else {
+ *base = (*base & 0xff00) | byte;
+ }
+}
+
+static inline void
+ao_usb_write_short(uint16_t data, uint32_t *base, uint16_t offset)
+{
+ base[offset>>1] = data;
+}
+
+static void
+ao_usb_write(const uint8_t *src, uint32_t *base, uint16_t bytes)
+{
+ uint16_t offset = 0;
+ if (!bytes)
+ return;
+ while (bytes >= 2) {
+ debug_data (" %02x %02x", src[0], src[1]);
+ ao_usb_write_short((uint16_t) ((uint16_t) (src[1] << 8) | (uint16_t) src[0]), base, offset);
+ offset += 2;
+ src += 2;
+ bytes -= 2;
+ }
+ if (bytes) {
+ debug_data (" %02x", src[0]);
+ ao_usb_write_byte(*src, base, offset);
+ }
+}
+
+static inline uint8_t
+ao_usb_read_byte(uint32_t *base, uint16_t offset)
+{
+ base += offset >> 1;
+ if (offset & 1)
+ return (*base >> 8) & 0xff;
+ else
+ return *base & 0xff;
+}
+
+static inline uint16_t
+ao_usb_read_short(uint32_t *base, uint16_t offset)
+{
+ return (uint16_t) (base[offset>>1]);
+}
+
+static void
+ao_usb_read(uint8_t *dst, uint32_t *base, uint16_t offset, uint16_t bytes)
+{
+ if (!bytes)
+ return;
+ if (offset & 1) {
+ *dst++ = ao_usb_read_byte(base, offset++);
+ debug_data (" %02x", dst[-1]);
+ bytes--;
+ }
+ while (bytes >= 2) {
+ uint16_t s = ao_usb_read_short(base, offset);
+ dst[0] = (uint8_t) s;
+ dst[1] = (uint8_t) (s >> 8);
+ debug_data (" %02x %02x", dst[0], dst[1]);
+ offset += 2;
+ dst += 2;
+ bytes -= 2;
+ }
+ if (bytes) {
+ *dst = ao_usb_read_byte(base, offset);
+ debug_data (" %02x", dst[0]);
+ }
+}
+
+/* Send an IN data packet */
+static void
+ao_usb_ep0_flush(void)
+{
+ uint8_t this_len;
+
+ /* Check to see if the endpoint is still busy */
+ if (ao_usb_epr_stat_tx(stm_usb.epr[0]) == STM_USB_EPR_STAT_TX_VALID) {
+ debug("EP0 not accepting IN data\n");
+ return;
+ }
+
+ this_len = ao_usb_ep0_in_len;
+ if (this_len > AO_USB_CONTROL_SIZE)
+ this_len = AO_USB_CONTROL_SIZE;
+
+ if (this_len < AO_USB_CONTROL_SIZE)
+ ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+ ao_usb_ep0_in_len -= this_len;
+
+ debug_data ("Flush EP0 len %d:", this_len);
+ ao_usb_write(ao_usb_ep0_in_data, ao_usb_ep0_tx_buffer, this_len);
+ debug_data ("\n");
+ ao_usb_ep0_in_data += this_len;
+
+ /* Mark the endpoint as TX valid to send the packet */
+ ao_usb_bdt[AO_USB_CONTROL_EPR].single.count_tx = this_len;
+ ao_usb_set_stat_tx(AO_USB_CONTROL_EPR, STM_USB_EPR_STAT_TX_VALID);
+ debug ("queue tx. epr 0 now %08x\n", stm_usb.epr[AO_USB_CONTROL_EPR]);
+}
+
+/* Read data from the ep0 OUT fifo */
+static void
+ao_usb_ep0_fill(void)
+{
+ uint16_t len = ao_usb_bdt[0].single.count_rx & STM_USB_BDT_COUNT_RX_COUNT_RX_MASK;
+
+ if (len > ao_usb_ep0_out_len)
+ len = ao_usb_ep0_out_len;
+ ao_usb_ep0_out_len -= (uint8_t) len;
+
+ /* Pull all of the data out of the packet */
+ debug_data ("Fill EP0 len %d:", len);
+ ao_usb_read(ao_usb_ep0_out_data, ao_usb_ep0_rx_buffer, 0, len);
+ debug_data ("\n");
+ ao_usb_ep0_out_data += len;
+
+ /* ACK the packet */
+ ao_usb_set_stat_rx(0, STM_USB_EPR_STAT_RX_VALID);
+}
+
+static void
+ao_usb_ep0_in_reset(void)
+{
+ ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
+ ao_usb_ep0_in_len = 0;
+}
+
+static void
+ao_usb_ep0_in_queue_byte(uint8_t a)
+{
+ if (ao_usb_ep0_in_len < sizeof (ao_usb_ep0_in_buf))
+ ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
+}
+
+static void
+ao_usb_ep0_in_set(const uint8_t *data, uint8_t len)
+{
+ ao_usb_ep0_in_data = data;
+ ao_usb_ep0_in_len = len;
+}
+
+static void
+ao_usb_ep0_out_set(uint8_t *data, uint8_t len)
+{
+ ao_usb_ep0_out_data = data;
+ ao_usb_ep0_out_len = len;
+}
+
+static void
+ao_usb_ep0_in_start(uint16_t max)
+{
+ /* Don't send more than asked for */
+ if (ao_usb_ep0_in_len > max)
+ ao_usb_ep0_in_len = (uint8_t) max;
+ ao_usb_ep0_flush();
+}
+
+struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
+
+/* Walk through the list of descriptors and find a match
+ */
+static void
+ao_usb_get_descriptor(uint16_t value, uint16_t length)
+{
+ const uint8_t *descriptor;
+ uint8_t type = (uint8_t) (value >> 8);
+ uint8_t index = (uint8_t) value;
+
+ descriptor = ao_usb_descriptors;
+ while (descriptor[0] != 0) {
+ if (descriptor[1] == type && index-- == 0) {
+ uint8_t len;
+ if (type == AO_USB_DESC_CONFIGURATION)
+ len = descriptor[2];
+ else
+ len = descriptor[0];
+ if (len > length)
+ len = (uint8_t) length;
+ ao_usb_ep0_in_set(descriptor, len);
+ break;
+ }
+ descriptor += descriptor[0];
+ }
+}
+
+static void
+ao_usb_ep0_setup(void)
+{
+ /* Pull the setup packet out of the fifo */
+ ao_usb_ep0_out_set((uint8_t *) &ao_usb_setup, 8);
+ ao_usb_ep0_fill();
+ if (ao_usb_ep0_out_len != 0) {
+ debug ("invalid setup packet length\n");
+ return;
+ }
+
+ if ((ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) || ao_usb_setup.length == 0)
+ ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+ else
+ ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
+
+ ao_usb_ep0_in_reset();
+
+ switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
+ case AO_USB_TYPE_STANDARD:
+ debug ("Standard setup packet\n");
+ switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
+ case AO_USB_RECIP_DEVICE:
+ debug ("Device setup packet\n");
+ switch(ao_usb_setup.request) {
+ case AO_USB_REQ_GET_STATUS:
+ debug ("get status\n");
+ ao_usb_ep0_in_queue_byte(0);
+ ao_usb_ep0_in_queue_byte(0);
+ break;
+ case AO_USB_REQ_SET_ADDRESS:
+ debug ("set address %d\n", ao_usb_setup.value);
+ ao_usb_address = (uint8_t) ao_usb_setup.value;
+ ao_usb_address_pending = 1;
+ break;
+ case AO_USB_REQ_GET_DESCRIPTOR:
+ debug ("get descriptor %d\n", ao_usb_setup.value);
+ ao_usb_get_descriptor(ao_usb_setup.value, ao_usb_setup.length);
+ break;
+ case AO_USB_REQ_GET_CONFIGURATION:
+ debug ("get configuration %d\n", ao_usb_configuration);
+ ao_usb_ep0_in_queue_byte(ao_usb_configuration);
+ break;
+ case AO_USB_REQ_SET_CONFIGURATION:
+ ao_usb_configuration = (uint8_t) ao_usb_setup.value;
+ debug ("set configuration %d\n", ao_usb_configuration);
+ ao_usb_set_configuration();
+ break;
+ }
+ break;
+ case AO_USB_RECIP_INTERFACE:
+ debug ("Interface setup packet\n");
+ switch(ao_usb_setup.request) {
+ case AO_USB_REQ_GET_STATUS:
+ ao_usb_ep0_in_queue_byte(0);
+ ao_usb_ep0_in_queue_byte(0);
+ break;
+ case AO_USB_REQ_GET_INTERFACE:
+ ao_usb_ep0_in_queue_byte(0);
+ break;
+ case AO_USB_REQ_SET_INTERFACE:
+ break;
+ }
+ break;
+ case AO_USB_RECIP_ENDPOINT:
+ debug ("Endpoint setup packet\n");
+ switch(ao_usb_setup.request) {
+ case AO_USB_REQ_GET_STATUS:
+ ao_usb_ep0_in_queue_byte(0);
+ ao_usb_ep0_in_queue_byte(0);
+ break;
+ }
+ break;
+ }
+ break;
+ case AO_USB_TYPE_CLASS:
+ debug ("Class setup packet\n");
+ switch (ao_usb_setup.request) {
+ case AO_USB_SET_LINE_CODING:
+ debug ("set line coding\n");
+ ao_usb_ep0_out_set((uint8_t *) &ao_usb_line_coding, 7);
+ break;
+ case AO_USB_GET_LINE_CODING:
+ debug ("get line coding\n");
+ ao_usb_ep0_in_set((const uint8_t *) &ao_usb_line_coding, 7);
+ break;
+ case AO_USB_SET_CONTROL_LINE_STATE:
+ break;
+ }
+ break;
+ }
+
+ /* If we're not waiting to receive data from the host,
+ * queue an IN response
+ */
+ if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+ ao_usb_ep0_in_start(ao_usb_setup.length);
+}
+
+static void
+ao_usb_ep0_handle(uint8_t receive)
+{
+ ao_usb_ep0_receive = 0;
+ if (receive & AO_USB_EP0_GOT_RESET) {
+ debug ("\treset\n");
+ ao_usb_set_ep0();
+ return;
+ }
+ if (receive & AO_USB_EP0_GOT_SETUP) {
+ debug ("\tsetup\n");
+ ao_usb_ep0_setup();
+ }
+ if (receive & AO_USB_EP0_GOT_RX_DATA) {
+ debug ("\tgot rx data\n");
+ if (ao_usb_ep0_state == AO_USB_EP0_DATA_OUT) {
+ ao_usb_ep0_fill();
+ if (ao_usb_ep0_out_len == 0) {
+ ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+ ao_usb_ep0_in_start(0);
+ }
+ }
+ }
+ if (receive & AO_USB_EP0_GOT_TX_ACK) {
+ debug ("\tgot tx ack\n");
+
+#if HAS_FLIGHT && AO_USB_FORCE_IDLE
+ ao_flight_force_idle = 1;
+#endif
+ /* Wait until the IN packet is received from addr 0
+ * before assigning our local address
+ */
+ if (ao_usb_address_pending)
+ ao_usb_set_address(ao_usb_address);
+ if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+ ao_usb_ep0_flush();
+ }
+}
+
+void
+stm_usb_lp_isr(void)
+{
+ uint32_t istr = stm_usb.istr;
+
+ if (istr & (1 << STM_USB_ISTR_CTR)) {
+ uint8_t ep = istr & STM_USB_ISTR_EP_ID_MASK;
+ uint32_t epr, epr_write;
+
+ /* Preserve the SW write bits, don't mess with most HW writable bits,
+ * clear the CTR_RX and CTR_TX bits
+ */
+ epr = stm_usb.epr[ep];
+ epr_write = epr;
+ epr_write &= STM_USB_EPR_PRESERVE_MASK;
+ epr_write |= STM_USB_EPR_INVARIANT;
+ epr_write &= ~(1UL << STM_USB_EPR_CTR_RX);
+ epr_write &= ~(1UL << STM_USB_EPR_CTR_TX);
+ stm_usb.epr[ep] = epr_write;
+
+ switch (ep) {
+ case 0:
+ ++control_count;
+ if (ao_usb_epr_ctr_rx(epr)) {
+ if (ao_usb_epr_setup(epr))
+ ao_usb_ep0_receive |= AO_USB_EP0_GOT_SETUP;
+ else
+ ao_usb_ep0_receive |= AO_USB_EP0_GOT_RX_DATA;
+ }
+ if (ao_usb_epr_ctr_tx(epr))
+ ao_usb_ep0_receive |= AO_USB_EP0_GOT_TX_ACK;
+ ao_usb_ep0_handle(ao_usb_ep0_receive);
+ break;
+ case AO_USB_OUT_EPR:
+ ++out_count;
+ if (ao_usb_epr_ctr_rx(epr)) {
+ _rx_dbg1("RX ISR", epr);
+ ao_usb_out_avail = 1;
+ _rx_dbg0("out avail set");
+ ao_wakeup(AO_USB_OUT_SLEEP_ADDR);
+ _rx_dbg0("stdin awoken");
+ }
+ break;
+ case AO_USB_IN_EPR:
+ ++in_count;
+ _tx_dbg1("TX ISR", epr);
+ if (ao_usb_epr_ctr_tx(epr)) {
+ ao_usb_in_pending = 0;
+ ao_wakeup(&ao_usb_in_pending);
+ }
+ break;
+ case AO_USB_INT_EPR:
+ ++int_count;
+ if (ao_usb_epr_ctr_tx(epr))
+ _ao_usb_set_stat_tx(AO_USB_INT_EPR, STM_USB_EPR_STAT_TX_NAK);
+ break;
+ }
+ return;
+ }
+
+ if (istr & (1 << STM_USB_ISTR_RESET)) {
+ ++reset_count;
+ stm_usb.istr &= ~(1UL << STM_USB_ISTR_RESET);
+ ao_usb_ep0_receive |= AO_USB_EP0_GOT_RESET;
+ ao_usb_ep0_handle(ao_usb_ep0_receive);
+ }
+}
+
+void
+stm_usb_wakeup_isr(void)
+{
+ /* USB wakeup, just clear the bit for now */
+ stm_usb.istr &= ~(1UL << STM_USB_ISTR_WKUP);
+}
+
+/* Queue the current IN buffer for transmission */
+static void
+_ao_usb_in_send(void)
+{
+ _tx_dbg0("in_send start");
+ debug ("send %d\n", ao_usb_tx_count);
+ while (ao_usb_in_pending)
+ ao_sleep(&ao_usb_in_pending);
+ ao_usb_in_pending = 1;
+ if (ao_usb_tx_count != AO_USB_IN_SIZE)
+ ao_usb_in_flushed = 1;
+ ao_usb_write(ao_usb_tx_buffer, ao_usb_in_tx_buffer, ao_usb_tx_count);
+ ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = ao_usb_tx_count;
+ ao_usb_tx_count = 0;
+ _ao_usb_set_stat_tx(AO_USB_IN_EPR, STM_USB_EPR_STAT_TX_VALID);
+ _tx_dbg0("in_send end");
+}
+
+/* Wait for a free IN buffer. Interrupts are blocked */
+static void
+_ao_usb_in_wait(void)
+{
+ for (;;) {
+ /* Check if the current buffer is writable */
+ if (ao_usb_tx_count < AO_USB_IN_SIZE)
+ break;
+
+ _tx_dbg0("in_wait top");
+ /* Wait for an IN buffer to be ready */
+ while (ao_usb_in_pending)
+ ao_sleep(&ao_usb_in_pending);
+ _tx_dbg0("in_wait bottom");
+ }
+}
+
+void
+ao_usb_flush(void)
+{
+ if (!ao_usb_running)
+ return;
+
+ /* Anytime we've sent a character since
+ * the last time we flushed, we'll need
+ * to send a packet -- the only other time
+ * we would send a packet is when that
+ * packet was full, in which case we now
+ * want to send an empty packet
+ */
+ ao_arch_block_interrupts();
+ while (!ao_usb_in_flushed) {
+ _tx_dbg0("flush top");
+ _ao_usb_in_send();
+ _tx_dbg0("flush end");
+ }
+ ao_arch_release_interrupts();
+}
+
+void
+ao_usb_putchar(char c)
+{
+ if (!ao_usb_running)
+ return;
+
+ ao_arch_block_interrupts();
+ _ao_usb_in_wait();
+
+ ao_usb_in_flushed = 0;
+ ao_usb_tx_buffer[ao_usb_tx_count++] = (uint8_t) c;
+
+ /* Send the packet when full */
+ if (ao_usb_tx_count == AO_USB_IN_SIZE) {
+ _tx_dbg0("putchar full");
+ _ao_usb_in_send();
+ _tx_dbg0("putchar flushed");
+ }
+ ao_arch_release_interrupts();
+}
+
+static void
+_ao_usb_out_recv(void)
+{
+ _rx_dbg0("out_recv top");
+ ao_usb_out_avail = 0;
+
+ ao_usb_rx_count = (uint8_t) (ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx & STM_USB_BDT_COUNT_RX_COUNT_RX_MASK);
+
+ _rx_dbg1("out_recv count", ao_usb_rx_count);
+ debug ("recv %d\n", ao_usb_rx_count);
+ debug_data("Fill OUT len %d:", ao_usb_rx_count);
+ ao_usb_read(ao_usb_rx_buffer, ao_usb_out_rx_buffer, 0, ao_usb_rx_count);
+ debug_data("\n");
+ ao_usb_rx_pos = 0;
+
+ /* ACK the packet */
+ _ao_usb_set_stat_rx(AO_USB_OUT_EPR, STM_USB_EPR_STAT_RX_VALID);
+}
+
+static int
+_ao_usb_pollchar(void)
+{
+ uint8_t c;
+
+ if (!ao_usb_running)
+ return AO_READ_AGAIN;
+
+ for (;;) {
+ if (ao_usb_rx_pos != ao_usb_rx_count)
+ break;
+
+ _rx_dbg0("poll check");
+ /* Check to see if a packet has arrived */
+ if (!ao_usb_out_avail) {
+ _rx_dbg0("poll none");
+ return AO_READ_AGAIN;
+ }
+ _ao_usb_out_recv();
+ }
+
+ /* Pull a character out of the fifo */
+ c = ao_usb_rx_buffer[ao_usb_rx_pos++];
+ return c;
+}
+
+char
+ao_usb_getchar(void)
+{
+ int c;
+
+ ao_arch_block_interrupts();
+ while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN)
+ ao_sleep(AO_USB_OUT_SLEEP_ADDR);
+ ao_arch_release_interrupts();
+ return (char) c;
+}
+
+#ifndef HAS_USB_DISABLE
+#define HAS_USB_DISABLE 1
+#endif
+
+#if HAS_USB_DISABLE
+void
+ao_usb_disable(void)
+{
+ ao_arch_block_interrupts();
+ stm_usb.cntr = (1 << STM_USB_CNTR_FRES);
+ stm_usb.istr = 0;
+
+#if HAS_USB_PULLUP
+ /* Disable USB pull-up */
+ ao_gpio_set(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, 0);
+#endif
+
+ /* Switch off the device */
+ stm_usb.cntr = (1 << STM_USB_CNTR_PDWN) | (1 << STM_USB_CNTR_FRES);
+
+ /* Disable the interface */
+ stm_rcc.apb1enr &= ~(1UL << STM_RCC_APB1ENR_USBEN);
+ ao_arch_release_interrupts();
+}
+#endif
+
+void
+ao_usb_enable(void)
+{
+ int t;
+
+#if HAS_USB_PULLUP
+ ao_gpio_set(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, 0);
+#endif
+
+ /* Enable USB device */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USBEN);
+
+ /* Do not touch the GPIOA configuration; USB takes priority
+ * over GPIO on pins A11 and A12, but if you select alternate
+ * input 10 (the documented correct selection), then USB is
+ * pulled low and doesn't work at all
+ */
+
+ ao_arch_block_interrupts();
+
+ /* Route interrupts */
+ stm_nvic_set_priority(STM_ISR_USB_LP_POS, AO_STM_NVIC_LOW_PRIORITY);
+ stm_nvic_set_enable(STM_ISR_USB_LP_POS);
+
+ ao_usb_configuration = 0;
+
+ stm_usb.cntr = (1 << STM_USB_CNTR_FRES);
+
+ /* Clear the power down bit */
+ stm_usb.cntr = 0;
+
+ /* Clear any spurious interrupts */
+ stm_usb.istr = 0;
+
+ debug ("ao_usb_enable\n");
+
+ /* Enable interrupts */
+ stm_usb.cntr = ((1 << STM_USB_CNTR_CTRM) |
+ (0 << STM_USB_CNTR_PMAOVRM) |
+ (0 << STM_USB_CNTR_ERRM) |
+ (0 << STM_USB_CNTR_WKUPM) |
+ (0 << STM_USB_CNTR_SUSPM) |
+ (1 << STM_USB_CNTR_RESETM) |
+ (0 << STM_USB_CNTR_SOFM) |
+ (0 << STM_USB_CNTR_ESOFM) |
+ (0 << STM_USB_CNTR_RESUME) |
+ (0 << STM_USB_CNTR_FSUSP) |
+ (0 << STM_USB_CNTR_LP_MODE) |
+ (0 << STM_USB_CNTR_PDWN) |
+ (0 << STM_USB_CNTR_FRES));
+
+ ao_arch_release_interrupts();
+
+ for (t = 0; t < 1000; t++)
+ ao_arch_nop();
+
+ /* Enable USB pull-up */
+#if HAS_USB_PULLUP
+ ao_gpio_set(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, 1);
+#endif
+}
+
+#if USB_ECHO
+struct ao_task ao_usb_echo_task;
+
+static void
+ao_usb_echo(void)
+{
+ char c;
+
+ for (;;) {
+ c = ao_usb_getchar();
+ ao_usb_putchar(c);
+ ao_usb_flush();
+ }
+}
+#endif
+
+#if USB_DEBUG
+static void
+ao_usb_irq(void)
+{
+ printf ("control: %d out: %d in: %d int: %d reset: %d\n",
+ control_count, out_count, in_count, int_count, reset_count);
+}
+
+const struct ao_cmds ao_usb_cmds[] = {
+ { ao_usb_irq, "I\0Show USB interrupt counts" },
+ { 0, NULL }
+};
+#endif
+
+void
+ao_usb_init(void)
+{
+#if HAS_USB_PULLUP
+ int i;
+ ao_enable_output(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, 0);
+ for (i = 0; i < 40000; i++)
+ ao_arch_nop();
+#endif
+ ao_usb_enable();
+
+ debug ("ao_usb_init\n");
+ ao_usb_ep0_state = AO_USB_EP0_IDLE;
+#if USB_ECHO
+ ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo");
+#endif
+#if USB_DEBUG
+ ao_cmd_register(&ao_usb_cmds[0]);
+#endif
+#if !USB_ECHO
+#if USE_USB_STDIO
+ ao_add_stdio(_ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
+#endif
+#endif
+}
+
+#if TX_DBG || RX_DBG
+
+struct ao_usb_dbg {
+ int line;
+ char *msg;
+ uint32_t value;
+ uint32_t prival;
+#if TX_DBG
+ uint16_t in_count;
+ uint32_t in_epr;
+ uint32_t in_pending;
+ uint32_t tx_count;
+ uint32_t in_flushed;
+#endif
+#if RX_DBG
+ uint8_t rx_count;
+ uint8_t rx_pos;
+ uint8_t out_avail;
+ uint32_t out_epr;
+#endif
+};
+
+#define NUM_USB_DBG 16
+
+static struct ao_usb_dbg dbg[NUM_USB_DBG];
+static int dbg_i;
+
+static void _dbg(int line, char *msg, uint32_t value)
+{
+ uint32_t prival;
+ dbg[dbg_i].line = line;
+ dbg[dbg_i].msg = msg;
+ dbg[dbg_i].value = value;
+#if AO_NONMASK_INTERRUPT
+ asm("mrs %0,basepri" : "=&r" (prival));
+#else
+ asm("mrs %0,primask" : "=&r" (prival));
+#endif
+ dbg[dbg_i].prival = prival;
+#if TX_DBG
+ dbg[dbg_i].in_count = in_count;
+ dbg[dbg_i].in_epr = stm_usb.epr[AO_USB_IN_EPR];
+ dbg[dbg_i].in_pending = ao_usb_in_pending;
+ dbg[dbg_i].tx_count = ao_usb_tx_count;
+ dbg[dbg_i].in_flushed = ao_usb_in_flushed;
+#endif
+#if RX_DBG
+ dbg[dbg_i].rx_count = ao_usb_rx_count;
+ dbg[dbg_i].rx_pos = ao_usb_rx_pos;
+ dbg[dbg_i].out_avail = ao_usb_out_avail;
+ dbg[dbg_i].out_epr = stm_usb.epr[AO_USB_OUT_EPR];
+#endif
+ if (++dbg_i == NUM_USB_DBG)
+ dbg_i = 0;
+}
+#endif
--- /dev/null
+#!/bin/sh
+#OPENOCD=openocd
+#OPENOCD=/usr/bin/openocd
+#OPENOCD=/local/src/openocd/src/openocd
+OPENOCD=/local/bin/openocd
+exec $OPENOCD \
+ -f interface/stlink.cfg \
+ -c 'transport select hla_swd' \
+ -f target/stm32f1x.cfg -c init \
+ -c 'reset halt' \
+ -c 'stm32f1x.cpu arm semihosting enable' \
+ "$@"
--- /dev/null
+stm_crc = 0x40023000;
+stm_flash = 0x40022000;
+stm_rcc = 0x40021000;
+stm_dma = 0x40020000;
+stm_usart1 = 0x40013800;
+stm_spi1 = 0x40013000;
+stm_tim1 = 0x40012c00;
+stm_adc2 = 0x40012800;
+stm_adc1 = 0x40012400;
+stm_gpioe = 0x40011800;
+stm_gpiod = 0x40011400;
+stm_gpioc = 0x40011000;
+stm_gpiob = 0x40010c00;
+stm_gpioa = 0x40010800;
+stm_exti = 0x40010400;
+stm_afio = 0x40010000;
+stm_pwr = 0x40007000;
+stm_bkp = 0x40006c00;
+stm_bxcan = 0x40006400;
+stm_usb_sram = 0x40006000;
+stm_usb = 0x40005c00;
+stm_i2c2 = 0x40005800;
+stm_i2c1 = 0x40005400;
+stm_usart3 = 0x40004800;
+stm_usart2 = 0x40004400;
+stm_spi2 = 0x40003800;
+stm_iwdg = 0x40003000;
+stm_wwdg = 0x40002c00;
+stm_rtc = 0x40002800;
+stm_tim4 = 0x40000800;
+stm_tim3 = 0x40000400;
+stm_tim2 = 0x40000000;
+
+stm_systick = 0xe000e010;
+
+stm_nvic = 0xe000e100;
+
+stm_scb = 0xe000ed00;
+
+stm_mpu = 0xe000ed90;
+
+stm_dbgmcu = 0xe0042000;
+
+/* data in system memory */
+stm_flash_data = 0x1ffff7e0;
--- /dev/null
+/*
+ * Copyright © 2023 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 _STM32F1_H_
+#define _STM32F1_H_
+
+#include <stdint.h>
+
+typedef volatile uint32_t vuint32_t;
+typedef volatile uint16_t vuint16_t;
+typedef volatile void * vvoid_t;
+
+struct stm_rcc {
+ vuint32_t cr;
+ vuint32_t cfgr;
+ vuint32_t cir;
+ vuint32_t apb2rstr;
+
+ vuint32_t apb1rstr;
+ vuint32_t ahbenr;
+ vuint32_t apb2enr;
+ vuint32_t apb1enr;
+
+ vuint32_t bdcr;
+ vuint32_t csr;
+ vuint32_t ahbstr;
+ vuint32_t cfgr2;
+};
+
+extern struct stm_rcc stm_rcc;
+
+//#define stm_rcc (*((struct stm_rcc *) 0x40021000))
+
+#define STM_RCC_CR_RTCPRE (29)
+#define STM_RCC_CR_RTCPRE_HSE_DIV_2 0
+#define STM_RCC_CR_RTCPRE_HSE_DIV_4 1
+#define STM_RCC_CR_RTCPRE_HSE_DIV_8 2
+#define STM_RCC_CR_RTCPRE_HSE_DIV_16 3
+#define STM_RCC_CR_RTCPRE_HSE_MASK 3UL
+
+#define STM_RCC_CR_PLL3RDY (29)
+#define STM_RCC_CR_PLL3ON (28)
+#define STM_RCC_CR_PLL2RDY (27)
+#define STM_RCC_CR_PLL2ON (26)
+#define STM_RCC_CR_PLLRDY (25)
+#define STM_RCC_CR_PLLON (24)
+#define STM_RCC_CR_CSSON (19)
+#define STM_RCC_CR_HSEBYP (18)
+#define STM_RCC_CR_HSERDY (17)
+#define STM_RCC_CR_HSEON (16)
+#define STM_RCC_CR_HSICAL (8)
+#define STM_RCC_CR_HSITRIM (3)
+#define STM_RCC_CR_HSIRDY (1)
+#define STM_RCC_CR_HSION (0)
+
+#define STM_RCC_CFGR_MCO (24)
+#define STM_RCC_CFGR_MCO_DISABLE 0
+#define STM_RCC_CFGR_MCO_SYSCLK 4
+#define STM_RCC_CFGR_MCO_HSI 5
+#define STM_RCC_CFGR_MCO_HSE 6
+#define STM_RCC_CFGR_MCO_PLL_2 7
+#define STM_RCC_CFGR_MCO_MASK 7UL
+
+#define STM_RCC_CFGR_USBPRE (22)
+#define STM_RCC_CFGR_USBPRE_1_5 0
+#define STM_RCC_CFGR_USBPRE_1 1
+
+#define STM_RCC_CFGR_PLLMUL (18)
+#define STM_RCC_CFGR_PLLMUL_2 0
+#define STM_RCC_CFGR_PLLMUL_3 1
+#define STM_RCC_CFGR_PLLMUL_4 2
+#define STM_RCC_CFGR_PLLMUL_5 3
+#define STM_RCC_CFGR_PLLMUL_6 4
+#define STM_RCC_CFGR_PLLMUL_7 5
+#define STM_RCC_CFGR_PLLMUL_8 6
+#define STM_RCC_CFGR_PLLMUL_9 7
+#define STM_RCC_CFGR_PLLMUL_10 8
+#define STM_RCC_CFGR_PLLMUL_11 9
+#define STM_RCC_CFGR_PLLMUL_12 10
+#define STM_RCC_CFGR_PLLMUL_13 11
+#define STM_RCC_CFGR_PLLMUL_14 12
+#define STM_RCC_CFGR_PLLMUL_15 13
+#define STM_RCC_CFGR_PLLMUL_16 14
+#define STM_RCC_CFGR_PLLMUL_MASK 0xfUL
+
+#define STM_RCC_CFGR_PLLXTPRE (17)
+#define STM_RCC_CFGR_PLLXTPRE_1 0
+#define STM_RCC_CFGR_PLLXTPRE_2 1
+#define STM_RCC_CFGR_PLLXTPRE_MASK 1UL
+
+#define STM_RCC_CFGR_PLLSRC (16)
+#define STM_RCC_CFGR_PLLSRC_HSI_2 0
+#define STM_RCC_CFGR_PLLSRC_HSE 1
+
+#define STM_RCC_CFGR_ADCPRE (14)
+#define STM_RCC_CFGR_ADCPRE_2 0
+#define STM_RCC_CFGR_ADCPRE_4 1
+#define STM_RCC_CFGR_ADCPRE_6 2
+#define STM_RCC_CFGR_ADCPRE_8 3
+#define STM_RCC_CFGR_ADCPRE_MASK 3UL
+
+#define STM_RCC_CFGR_PPRE2 (11)
+#define STM_RCC_CFGR_PPRE2_DIV_1 0
+#define STM_RCC_CFGR_PPRE2_DIV_2 4
+#define STM_RCC_CFGR_PPRE2_DIV_4 5
+#define STM_RCC_CFGR_PPRE2_DIV_8 6
+#define STM_RCC_CFGR_PPRE2_DIV_16 7
+#define STM_RCC_CFGR_PPRE2_MASK 7UL
+
+#define STM_RCC_CFGR_PPRE1 (8)
+#define STM_RCC_CFGR_PPRE1_DIV_1 0
+#define STM_RCC_CFGR_PPRE1_DIV_2 4
+#define STM_RCC_CFGR_PPRE1_DIV_4 5
+#define STM_RCC_CFGR_PPRE1_DIV_8 6
+#define STM_RCC_CFGR_PPRE1_DIV_16 7
+#define STM_RCC_CFGR_PPRE1_MASK 7UL
+
+#define STM_RCC_CFGR_HPRE (4)
+#define STM_RCC_CFGR_HPRE_DIV_1 0
+#define STM_RCC_CFGR_HPRE_DIV_2 8
+#define STM_RCC_CFGR_HPRE_DIV_4 9
+#define STM_RCC_CFGR_HPRE_DIV_8 0xa
+#define STM_RCC_CFGR_HPRE_DIV_16 0xb
+#define STM_RCC_CFGR_HPRE_DIV_64 0xc
+#define STM_RCC_CFGR_HPRE_DIV_128 0xd
+#define STM_RCC_CFGR_HPRE_DIV_256 0xe
+#define STM_RCC_CFGR_HPRE_DIV_512 0xf
+#define STM_RCC_CFGR_HPRE_MASK 0xfUL
+
+#define STM_RCC_CFGR_SWS (2)
+#define STM_RCC_CFGR_SWS_HSI 0
+#define STM_RCC_CFGR_SWS_HSE 1
+#define STM_RCC_CFGR_SWS_PLL 2
+#define STM_RCC_CFGR_SWS_MASK 3UL
+
+#define STM_RCC_CFGR_SW (0)
+#define STM_RCC_CFGR_SW_HSI 0
+#define STM_RCC_CFGR_SW_HSE 1
+#define STM_RCC_CFGR_SW_PLL 2
+#define STM_RCC_CFGR_SW_MASK 3UL
+
+#define STM_RCC_AHBENR_CRCEN 6
+#define STM_RCC_AHBENR_FLITFEN 4
+#define STM_RCC_AHBENR_SRAMEN 2
+#define STM_RCC_AHBENR_DMA2EN 1
+#define STM_RCC_AHBENR_DMA1EN 0
+
+
+#define STM_RCC_APB2ENR_USART1EN 14
+#define STM_RCC_APB2ENR_SPI1EN 12
+#define STM_RCC_APB2ENR_TIM1EN 11
+#define STM_RCC_APB2ENR_ADC2EN 10
+#define STM_RCC_APB2ENR_ADC1EN 9
+#define STM_RCC_APB2ENR_IOPEEN 6
+#define STM_RCC_APB2ENR_IOPDEN 5
+#define STM_RCC_APB2ENR_IOPCEN 4
+#define STM_RCC_APB2ENR_IOPBEN 3
+#define STM_RCC_APB2ENR_IOPAEN 2
+#define STM_RCC_APB2ENR_AFIOEN 0
+
+#define STM_RCC_APB1ENR_DACEN 29
+#define STM_RCC_APB1ENR_PWREN 28
+#define STM_RCC_APB1ENR_BKPEN 27
+#define STM_RCC_APB1ENR_CANEN 26
+#define STM_RCC_APB1ENR_USBEN 23
+#define STM_RCC_APB1ENR_I2C2EN 22
+#define STM_RCC_APB1ENR_I2C1EN 21
+#define STM_RCC_APB1ENR_UART5EN 20
+#define STM_RCC_APB1ENR_UART4EN 19
+#define STM_RCC_APB1ENR_USART3EN 18
+#define STM_RCC_APB1ENR_USART2EN 17
+#define STM_RCC_APB1ENR_SPI3EN 15
+#define STM_RCC_APB1ENR_SPI2EN 14
+#define STM_RCC_APB1ENR_WWDGEN 11
+#define STM_RCC_APB1ENR_TIM7EN 5
+#define STM_RCC_APB1ENR_TIM6EN 4
+#define STM_RCC_APB1ENR_TIM5EN 3
+#define STM_RCC_APB1ENR_TIM4EN 2
+#define STM_RCC_APB1ENR_TIM3EN 1
+#define STM_RCC_APB1ENR_TIM2EN 0
+
+#define STM_RCC_CSR_LPWRRSTF (31)
+#define STM_RCC_CSR_WWDGRSTF (30)
+#define STM_RCC_CSR_IWDGRSTF (29)
+#define STM_RCC_CSR_SFTRSTF (28)
+#define STM_RCC_CSR_PORRSTF (27)
+#define STM_RCC_CSR_PINRSTF (26)
+#define STM_RCC_CSR_RMVF (24)
+#define STM_RCC_CSR_LSIRDY (1)
+#define STM_RCC_CSR_LSION (0)
+
+struct stm_systick {
+ vuint32_t ctrl;
+ vuint32_t load;
+ vuint32_t val;
+ vuint32_t calib;
+};
+
+extern struct stm_systick stm_systick;
+
+//#define stm_systick (*((struct stm_systick *) 0xe000e010))
+
+#define STM_SYSTICK_CTRL_ENABLE 0
+#define STM_SYSTICK_CTRL_TICKINT 1
+#define STM_SYSTICK_CTRL_CLKSOURCE 2
+#define STM_SYSTICK_CTRL_CLKSOURCE_HCLK_8 0
+#define STM_SYSTICK_CTRL_CLKSOURCE_HCLK 1
+#define STM_SYSTICK_CTRL_COUNTFLAG 16
+
+/* The NVIC starts at 0xe000e100, so add that to the offsets to find the absolute address */
+
+struct stm_nvic {
+ vuint32_t iser[3]; /* 0x000 0xe000e100 Set Enable Register */
+
+ uint8_t _unused00c[0x080 - 0x00c];
+
+ vuint32_t icer[3]; /* 0x080 0xe000e180 Clear Enable Register */
+
+ uint8_t _unused08c[0x100 - 0x08c];
+
+ vuint32_t ispr[3]; /* 0x100 0xe000e200 Set Pending Register */
+
+ uint8_t _unused10c[0x180 - 0x10c];
+
+ vuint32_t icpr[3]; /* 0x180 0xe000e280 Clear Pending Register */
+
+ uint8_t _unused18c[0x200 - 0x18c];
+
+ vuint32_t iabr[3]; /* 0x200 0xe000e300 Active Bit Register */
+
+ uint8_t _unused20c[0x300 - 0x20c];
+
+ vuint32_t ipr[31]; /* 0x300 0xe000e400 Priority Register */
+
+ uint8_t _unused37c[0xe00 - 0x37c]; /* covers SCB */
+
+ vuint32_t stir; /* 0xe00 0xe000ee00 Software Trigger Interrupt Register */
+};
+
+extern struct stm_nvic stm_nvic;
+
+//#define stm_nvic (*((struct stm_nvic *) 0xe000e100))
+
+#define IRQ_REG(irq) ((irq) >> 5)
+#define IRQ_BIT(irq) ((irq) & 0x1f)
+#define IRQ_MASK(irq) (1 << IRQ_BIT(irq))
+#define IRQ_BOOL(v,irq) (((v) >> IRQ_BIT(irq)) & 1)
+
+static inline void
+stm_nvic_set_enable(int irq) {
+ stm_nvic.iser[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline void
+stm_nvic_clear_enable(int irq) {
+ stm_nvic.icer[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline int
+stm_nvic_enabled(int irq) {
+ return IRQ_BOOL(stm_nvic.iser[IRQ_REG(irq)], irq);
+}
+
+static inline void
+stm_nvic_set_pending(int irq) {
+ stm_nvic.ispr[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline void
+stm_nvic_clear_pending(int irq) {
+ stm_nvic.icpr[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline int
+stm_nvic_pending(int irq) {
+ return IRQ_BOOL(stm_nvic.ispr[IRQ_REG(irq)], irq);
+}
+
+static inline int
+stm_nvic_active(int irq) {
+ return IRQ_BOOL(stm_nvic.iabr[IRQ_REG(irq)], irq);
+}
+
+#define IRQ_PRIO_REG(irq) ((irq) >> 2)
+#define IRQ_PRIO_BIT(irq) (((irq) & 3) << 3)
+#define IRQ_PRIO_MASK(irq) (0xff << IRQ_PRIO_BIT(irq))
+
+static inline void
+stm_nvic_set_priority(int irq, uint8_t prio) {
+ int n = IRQ_PRIO_REG(irq);
+ uint32_t v;
+
+ v = stm_nvic.ipr[n];
+ v &= (uint32_t) ~IRQ_PRIO_MASK(irq);
+ v |= (prio) << IRQ_PRIO_BIT(irq);
+ stm_nvic.ipr[n] = v;
+}
+
+static inline uint8_t
+stm_nvic_get_priority(int irq) {
+ return (stm_nvic.ipr[IRQ_PRIO_REG(irq)] >> IRQ_PRIO_BIT(irq)) & IRQ_PRIO_MASK(0);
+}
+
+struct stm_scb {
+ vuint32_t cpuid;
+ vuint32_t icsr;
+ vuint32_t vtor;
+ vuint32_t aircr;
+
+ vuint32_t scr;
+ vuint32_t ccr;
+ vuint32_t shpr1;
+ vuint32_t shpr2;
+
+ vuint32_t shpr3;
+ vuint32_t shcrs;
+ vuint32_t cfsr;
+ vuint32_t hfsr;
+
+ uint32_t unused_30;
+ vuint32_t mmar;
+ vuint32_t bfar;
+};
+
+extern struct stm_scb stm_scb;
+
+#define STM_SCB_AIRCR_VECTKEY 16
+#define STM_SCB_AIRCR_VECTKEY_KEY 0x05fa
+#define STM_SCB_AIRCR_PRIGROUP 8
+#define STM_SCB_AIRCR_SYSRESETREQ 2
+#define STM_SCB_AIRCR_VECTCLRACTIVE 1
+#define STM_SCB_AIRCR_VECTRESET 0
+
+struct stm_dbgmcu {
+ uint32_t idcode;
+};
+
+extern struct stm_dbgmcu stm_dbgmcu;
+
+static inline uint16_t
+stm_dev_id(void) {
+ return stm_dbgmcu.idcode & 0xfff;
+}
+
+struct stm_flash {
+ vuint32_t acr;
+ vuint32_t keyr;
+ vuint32_t optkeyr;
+ vuint32_t sr;
+
+ vuint32_t cr;
+ vuint32_t ar;
+ uint32_t _unused018;
+ vuint32_t obr;
+
+ vuint32_t wrpr;
+};
+
+extern struct stm_flash stm_flash;
+
+//#define stm_flash (*((struct stm_flash *) 0x40022000))
+
+#define STM_FLASH_ACR_PRFTBS 5
+#define STM_FLASH_ACR_PRFTBE 4
+#define STM_FLASH_ACR_HLFCYA 3
+#define STM_FLASH_ACR_LATENCY 0
+#define STM_FLASH_ACR_LATENCY_0 0
+#define STM_FLASH_ACR_LATENCY_1 1
+#define STM_FLASH_ACR_LATENCY_2 2
+
+#define STM_FLASH_SR_EOP 5
+#define STM_FLASH_SR_WRPRTERR 4
+#define STM_FLASH_SR_PGERR 2
+#define STM_FLASH_SR_BSY 0
+
+#define STM_FLASH_CR_EOPIE 12
+#define STM_FLASH_CR_ERRIE 10
+#define STM_FLASH_CR_OPTWRE 9
+#define STM_FLASH_CR_LOCK 7
+#define STM_FLASH_CR_STRT 6
+#define STM_FLASH_CR_OPTER 5
+#define STM_FLASH_CR_OPTPG 4
+#define STM_FLASH_CR_MER 2
+#define STM_FLASH_CR_PER 1
+#define STM_FLASH_CR_PG 0
+
+#define STM_FLASH_RDPRT_KEY 0x00A5
+#define STM_FLASH_KEYR_KEY1 0x45670123
+#define STM_FLASH_KEYR_KEY2 0xCDEF89AB
+
+
+struct stm_flash_data {
+ vuint16_t f_size;
+ vuint16_t unused02;
+ vuint32_t unused04;
+ vuint32_t device_id[3];
+};
+
+extern struct stm_flash_data stm_flash_data;
+
+static inline uint32_t stm_flash_size(void) { return (uint32_t) stm_flash_data.f_size * 1024; }
+
+//#define stm_flash_data (*((struct stm_flash_data *) 0x1ffff7e0))
+
+struct stm_gpio {
+ vuint32_t cr[2];
+ vuint32_t idr;
+ vuint32_t odr;
+
+ vuint32_t bsrr;
+ vuint32_t brr;
+ vuint32_t lckr;
+};
+
+#define STM_GPIO_CR(y) ((uint8_t) (y) >> 3)
+#define STM_GPIO_CR_CNF(y) ((((uint8_t) (y) & 7) << 2) + 2)
+#define STM_GPIO_CR_CNF_INPUT_ANALOG 0
+#define STM_GPIO_CR_CNF_INPUT_FLOATING 1
+#define STM_GPIO_CR_CNF_INPUT_PULL 2
+#define STM_GPIO_CR_CNF_OUTPUT_PUSH_PULL 0
+#define STM_GPIO_CR_CNF_OUTPUT_OPEN_DRAIN 1
+#define STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL 2
+#define STM_GPIO_CR_CNF_OUTPUT_AF_OPEN_DRAIN 3
+#define STM_GPIO_CR_CNF_MASK 3U
+#define STM_GPIO_CR_MODE(y) ((((y) & 7) << 2))
+#define STM_GPIO_CR_MODE_INPUT 0
+#define STM_GPIO_CR_MODE_OUTPUT_10MHZ 1
+#define STM_GPIO_CR_MODE_OUTPUT_2MHZ 2
+#define STM_GPIO_CR_MODE_OUTPUT_50MHZ 3
+#define STM_GPIO_CR_MODE_MASK 3U
+
+static inline void
+stm_gpio_conf(struct stm_gpio *gpio, int pin, uint8_t mode, uint8_t cnf)
+{
+ uint8_t cr = STM_GPIO_CR(pin);
+ uint32_t v = gpio->cr[cr];
+
+ v &= ~((STM_GPIO_CR_CNF_MASK << STM_GPIO_CR_CNF(pin)) |
+ (STM_GPIO_CR_MODE_MASK << STM_GPIO_CR_MODE(pin)));
+ v |= (mode << STM_GPIO_CR_MODE(pin)) | (cnf << STM_GPIO_CR_CNF(pin));
+ gpio->cr[cr] = v;
+}
+
+static inline void
+stm_gpio_set(struct stm_gpio *gpio, int pin, uint8_t value) {
+ /* Use the bit set/reset register to do this atomically */
+ gpio->bsrr = ((uint32_t) (value ^ 1) << (pin + 16)) | ((uint32_t) value << pin);
+}
+
+static inline void
+stm_gpio_set_mask(struct stm_gpio *gpio, uint16_t bits, uint16_t mask) {
+ /* Use the bit set/reset register to do this atomically */
+ gpio->bsrr = ((uint32_t) (~bits & mask) << 16) | ((uint32_t) (bits & mask));
+}
+
+static inline void
+stm_gpio_set_bits(struct stm_gpio *gpio, uint16_t bits) {
+ gpio->bsrr = bits;
+}
+
+static inline void
+stm_gpio_clr_bits(struct stm_gpio *gpio, uint16_t bits) {
+ gpio->bsrr = ((uint32_t) bits) << 16;
+}
+
+static inline uint8_t
+stm_gpio_get(struct stm_gpio *gpio, int pin) {
+ return (gpio->idr >> pin) & 1;
+}
+
+static inline uint16_t
+stm_gpio_get_all(struct stm_gpio *gpio) {
+ return (uint16_t) 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;
+
+#define stm_gpioe (*((struct stm_gpio *) 0x40011800))
+#define stm_gpiod (*((struct stm_gpio *) 0x40011400))
+#define stm_gpioc (*((struct stm_gpio *) 0x40011000))
+#define stm_gpiob (*((struct stm_gpio *) 0x40010c00))
+#define stm_gpioa (*((struct stm_gpio *) 0x40010800))
+
+struct stm_afio {
+ vuint32_t evcr;
+ vuint32_t mapr;
+ vuint32_t exticr[4];
+ vuint32_t mapr2;
+};
+
+extern struct stm_afio stm_afio;
+
+#define stm_afio (*((struct stm_afio *) 0x40010000))
+
+#define STM_AFIO_MAPR_SWJ_CFG 24
+#define STM_AFIO_MAPR_SWJ_CFG_FULL_SWJ 0
+#define STM_AFIO_MAPR_SWJ_CFG_FULL_SWJ_NO_NJTRST 1
+#define STM_AFIO_MAPR_SWJ_CFG_SW_DP 2
+#define STM_AFIO_MAPR_SWJ_CFG_DISABLE 4
+#define STM_AFIO_MAPR_SWJ_CFG_MASK 7UL
+#define STM_AFIO_MAPR_ADC2_ETRGREG_REMAP 20
+#define STM_AFIO_MAPR_ADC2_ETRGINJ_REMAP 19
+#define STM_AFIO_MAPR_ADC1_ETRGREG_REMAP 18
+#define STM_AFIO_MAPR_ADC1_ETRGINJ_REMAP 17
+#define STM_AFIO_MAPR_TIM5CH4_IREMAP 16
+#define STM_AFIO_MAPR_PD01_REMAP 15
+#define STM_AFIO_MAPR_CAN_REMAP 13
+#define STM_AFIO_MAPR_CAN_REMAP_PA11_PA12 0
+#define STM_AFIO_MAPR_CAN_REMAP_PB8_PB9 2
+#define STM_AFIO_MAPR_CAN_REMAP_PD0_PD1 3
+#define STM_AFIO_MAPR_CAN_REMAP_MASK 3UL
+#define STM_AFIO_MAPR_TIM4_REMAP 12
+#define STM_AFIO_MAPR_TIM3_REMAP 10
+#define STM_AFIO_MAPR_TIM3_REMAP_PA6_PA7_PB0_PB1 0
+#define STM_AFIO_MAPR_TIM3_REMAP_PB4_PB5_PB0_PB1 2
+#define STM_AFIO_MAPR_TIM3_REMAP_PC6_PC7_PC8_PC9 3
+#define STM_AFIO_MAPR_TIM3_REMAP_MASK 3UL
+#define STM_AFIO_MAPR_TIM2_REMAP 8
+#define STM_AFIO_MAPR_TIM2_REMAP_PA0_PA1_PA2_PA3 0
+#define STM_AFIO_MAPR_TIM2_REMAP_PA15_PB3_PA2_PA3 1
+#define STM_AFIO_MAPR_TIM2_REMAP_PA0_PA1_PB10_PB11 2
+#define STM_AFIO_MAPR_TIM2_REMAP_PA15_PB3_PB10_PB11 3
+#define STM_AFIO_MAPR_TIM2_REMAP_MASK 3UL
+#define STM_AFIO_MAPR_TIM1_REMAP 6
+#define STM_AFIO_MAPR_TIM1_REMAP_PA12_PA8_PA9_PA10_PA11_PB12_PB13_PB14_PB15 0
+#define STM_AFIO_MAPR_TIM1_REMAP_PA12_PA8_PA9_PA10_PA11_PA6_PA7_PB0_PB1 1
+#define STM_AFIO_MAPR_TIM1_REMAP_PE7_PE9_PE11_PE13_PE14_PE15_PE8_PE10_PE12 3
+#define STM_AFIO_MAPR_TIM1_REMAP_MASK 3
+#define STM_AFIO_MAPR_USART3_REMAP 4
+#define STM_AFIO_MAPR_USART3_REMAP_PB10_PB11_PB12_PB13_PB14 0
+#define STM_AFIO_MAPR_USART3_REMAP_PC10_PC11_PC12_PB13_PB14 1
+#define STM_AFIO_MAPR_USART3_REMAP_PD8_PD9_PD10_PD11_PD12 3
+#define STM_AFIO_MAPR_USART3_REMAP_MASK 3
+#define STM_AFIO_MAPR_USART2_REMAP 3
+#define STM_AFIO_MAPR_USART2_REMAP_PA0_PA1_PA2_PA3_PA4 0
+#define STM_AFIO_MAPR_USART2_REMAP_PD3_PD4_PD5_PD6_PD7 1
+#define STM_AFIO_MAPR_USART2_REMAP_MASK 1
+#define STM_AFIO_MAPR_USART1_REMAP 2
+#define STM_AFIO_MAPR_USART1_REMAP_PA9_PA10 0
+#define STM_AFIO_MAPR_USART1_REMAP_PB6_PB7 1
+#define STM_AFIO_MAPR_USART1_REMAP_MASK 1
+#define STM_AFIO_MAPR_I2C1_REMAP 1
+#define STM_AFIO_MAPR_I2C1_REMAP_PB6_PB7 0
+#define STM_AFIO_MAPR_I2C1_REMAP_PB8_PB9 1
+#define STM_AFIO_MAPR_I2C1_REMAP_MASK 1
+#define STM_AFIO_MAPR_SPI1_REMAP 0
+#define STM_AFIO_MAPR_SPI1_REMAP_PA4_PA5_PA6_PA7 0
+#define STM_AFIO_MAPR_SPI1_REMAP_PA15_PB3_PB4_PB5 1
+#define STM_AFIO_MAPR_SPI1_REMAP_MASK 1
+
+#define STM_AFIO_EXTICR_PA 0
+#define STM_AFIO_EXTICR_PB 1
+#define STM_AFIO_EXTICR_PC 2
+#define STM_AFIO_EXTICR_PD 3
+#define STM_AFIO_EXTICR_PE 4
+#define STM_AFIO_EXTICR_PF 5
+#define STM_AFIO_EXTICR_PG 6
+
+static inline void
+stm_set_afio_mapr(uint8_t bit, uint32_t val, uint32_t mask) {
+ uint32_t mapr = stm_afio.mapr;
+
+ mapr &= ~(mask << bit);
+ mapr |= (val << bit);
+ stm_afio.mapr = mapr;
+}
+
+struct stm_usart {
+ vuint32_t sr; /* status register */
+ vuint32_t dr; /* data register */
+ vuint32_t brr; /* baud rate register */
+ vuint32_t cr1; /* control register 1 */
+
+ vuint32_t cr2; /* control register 2 */
+ vuint32_t cr3; /* control register 3 */
+ vuint32_t gtpr; /* guard time and prescaler */
+};
+
+extern struct stm_usart stm_usart1;
+extern struct stm_usart stm_usart2;
+extern struct stm_usart stm_usart3;
+
+//#define stm_usart1 (*((struct stm_usart *) 0x40013800))
+//#define stm_usart2 (*((struct stm_usart *) 0x40004800))
+//#define stm_usart3 (*((struct stm_usart *) 0x40004400))
+
+#define STM_USART_SR_CTS (9) /* CTS flag */
+#define STM_USART_SR_LBD (8) /* LIN break detection flag */
+#define STM_USART_SR_TXE (7) /* Transmit data register empty */
+#define STM_USART_SR_TC (6) /* Transmission complete */
+#define STM_USART_SR_RXNE (5) /* Read data register not empty */
+#define STM_USART_SR_IDLE (4) /* IDLE line detected */
+#define STM_USART_SR_ORE (3) /* Overrun error */
+#define STM_USART_SR_NE (2) /* Noise detected flag */
+#define STM_USART_SR_FE (1) /* Framing error */
+#define STM_USART_SR_PE (0) /* Parity error */
+
+#define STM_USART_BRR_DIV_MANTISSA (4)
+#define STM_USART_BRR_DIV_FRACTION (0)
+
+#define STM_USART_CR1_UE (13) /* USART enable */
+#define STM_USART_CR1_M (12) /* Word length */
+#define STM_USART_CR1_WAKE (11) /* Wakeup method */
+#define STM_USART_CR1_PCE (10) /* Parity control enable */
+#define STM_USART_CR1_PS (9) /* Parity selection */
+#define STM_USART_CR1_PEIE (8) /* PE interrupt enable */
+#define STM_USART_CR1_TXEIE (7) /* TXE interrupt enable */
+#define STM_USART_CR1_TCIE (6) /* Transmission complete interrupt enable */
+#define STM_USART_CR1_RXNEIE (5) /* RXNE interrupt enable */
+#define STM_USART_CR1_IDLEIE (4) /* IDLE interrupt enable */
+#define STM_USART_CR1_TE (3) /* Transmitter enable */
+#define STM_USART_CR1_RE (2) /* Receiver enable */
+#define STM_USART_CR1_RWU (1) /* Receiver wakeup */
+#define STM_USART_CR1_SBK (0) /* Send break */
+
+#define STM_USART_CR2_LINEN (14) /* LIN mode enable */
+#define STM_USART_CR2_STOP (12) /* STOP bits */
+#define STM_USART_CR2_STOP_MASK 3UL
+#define STM_USART_CR2_STOP_1 0
+#define STM_USART_CR2_STOP_0_5 1
+#define STM_USART_CR2_STOP_2 2
+#define STM_USART_CR2_STOP_1_5 3
+
+#define STM_USART_CR2_CLKEN (11) /* Clock enable */
+#define STM_USART_CR2_CPOL (10) /* Clock polarity */
+#define STM_USART_CR2_CPHA (9) /* Clock phase */
+#define STM_USART_CR2_LBCL (8) /* Last bit clock pulse */
+#define STM_USART_CR2_LBDIE (6) /* LIN break detection interrupt enable */
+#define STM_USART_CR2_LBDL (5) /* lin break detection length */
+#define STM_USART_CR2_ADD (0)
+#define STM_USART_CR2_ADD_MASK 0xfUL
+
+#define STM_USART_CR3_CTSIE (10) /* CTS interrupt enable */
+#define STM_USART_CR3_CTSE (9) /* CTS enable */
+#define STM_USART_CR3_RTSE (8) /* RTS enable */
+#define STM_USART_CR3_DMAT (7) /* DMA enable transmitter */
+#define STM_USART_CR3_DMAR (6) /* DMA enable receiver */
+#define STM_USART_CR3_SCEN (5) /* Smartcard mode enable */
+#define STM_USART_CR3_NACK (4) /* Smartcard NACK enable */
+#define STM_USART_CR3_HDSEL (3) /* Half-duplex selection */
+#define STM_USART_CR3_IRLP (2) /* IrDA low-power */
+#define STM_USART_CR3_IREN (1) /* IrDA mode enable */
+#define STM_USART_CR3_EIE (0) /* Error interrupt enable */
+
+struct stm_usb {
+ vuint32_t epr[8];
+ uint8_t reserved_20[0x40 - 0x20];
+ vuint32_t cntr;
+ vuint32_t istr;
+ vuint32_t fnr;
+ vuint32_t daddr;
+ vuint32_t btable;
+};
+
+/*
+ * USB DM: PA11
+ * USB DP: PA12
+ *
+ * Need a pull-up on a separate GPIO
+ */
+#define STM_USB_EPR_CTR_RX 15
+#define STM_USB_EPR_CTR_RX_WRITE_INVARIANT 1
+#define STM_USB_EPR_DTOG_RX 14
+#define STM_USB_EPR_DTOG_RX_WRITE_INVARIANT 0
+#define STM_USB_EPR_STAT_RX 12
+#define STM_USB_EPR_STAT_RX_DISABLED 0
+#define STM_USB_EPR_STAT_RX_STALL 1
+#define STM_USB_EPR_STAT_RX_NAK 2
+#define STM_USB_EPR_STAT_RX_VALID 3
+#define STM_USB_EPR_STAT_RX_MASK 3UL
+#define STM_USB_EPR_STAT_RX_WRITE_INVARIANT 0
+#define STM_USB_EPR_SETUP 11
+#define STM_USB_EPR_EP_TYPE 9
+#define STM_USB_EPR_EP_TYPE_BULK 0
+#define STM_USB_EPR_EP_TYPE_CONTROL 1
+#define STM_USB_EPR_EP_TYPE_ISO 2
+#define STM_USB_EPR_EP_TYPE_INTERRUPT 3
+#define STM_USB_EPR_EP_TYPE_MASK 3UL
+#define STM_USB_EPR_EP_KIND 8
+#define STM_USB_EPR_EP_KIND_DBL_BUF 1 /* Bulk */
+#define STM_USB_EPR_EP_KIND_STATUS_OUT 1 /* Control */
+#define STM_USB_EPR_CTR_TX 7
+#define STM_USB_CTR_TX_WRITE_INVARIANT 1
+#define STM_USB_EPR_DTOG_TX 6
+#define STM_USB_EPR_DTOG_TX_WRITE_INVARIANT 0
+#define STM_USB_EPR_STAT_TX 4
+#define STM_USB_EPR_STAT_TX_DISABLED 0
+#define STM_USB_EPR_STAT_TX_STALL 1
+#define STM_USB_EPR_STAT_TX_NAK 2
+#define STM_USB_EPR_STAT_TX_VALID 3
+#define STM_USB_EPR_STAT_TX_WRITE_INVARIANT 0
+#define STM_USB_EPR_STAT_TX_MASK 3UL
+#define STM_USB_EPR_EA 0
+#define STM_USB_EPR_EA_MASK 0xfUL
+
+#define STM_USB_CNTR_CTRM 15
+#define STM_USB_CNTR_PMAOVRM 14
+#define STM_USB_CNTR_ERRM 13
+#define STM_USB_CNTR_WKUPM 12
+#define STM_USB_CNTR_SUSPM 11
+#define STM_USB_CNTR_RESETM 10
+#define STM_USB_CNTR_SOFM 9
+#define STM_USB_CNTR_ESOFM 8
+#define STM_USB_CNTR_RESUME 4
+#define STM_USB_CNTR_FSUSP 3
+#define STM_USB_CNTR_LP_MODE 2
+#define STM_USB_CNTR_PDWN 1
+#define STM_USB_CNTR_FRES 0
+
+#define STM_USB_ISTR_CTR 15
+#define STM_USB_ISTR_PMAOVR 14
+#define STM_USB_ISTR_ERR 13
+#define STM_USB_ISTR_WKUP 12
+#define STM_USB_ISTR_SUSP 11
+#define STM_USB_ISTR_RESET 10
+#define STM_USB_ISTR_SOF 9
+#define STM_USB_ISTR_ESOF 8
+#define STM_USB_ISTR_DIR 4
+#define STM_USB_ISTR_EP_ID 0
+#define STM_USB_ISTR_EP_ID_MASK 0xfUL
+
+#define STM_USB_FNR_RXDP 15
+#define STM_USB_FNR_RXDM 14
+#define STM_USB_FNR_LCK 13
+#define STM_USB_FNR_LSOF 11
+#define STM_USB_FNR_LSOF_MASK 0x3UL
+#define STM_USB_FNR_FN 0
+#define STM_USB_FNR_FN_MASK 0x7ffUL
+
+#define STM_USB_DADDR_EF 7
+#define STM_USB_DADDR_ADD 0
+#define STM_USB_DADDR_ADD_MASK 0x7fUL
+
+extern struct stm_usb stm_usb;
+
+#define stm_usb (*((struct stm_usb *) 0x40005c00))
+
+union stm_usb_bdt {
+ struct {
+ vuint32_t addr_tx;
+ vuint32_t count_tx;
+ vuint32_t addr_rx;
+ vuint32_t count_rx;
+ } single;
+ struct {
+ vuint32_t addr;
+ vuint32_t count;
+ } double_tx[2];
+ struct {
+ vuint32_t addr;
+ vuint32_t count;
+ } double_rx[2];
+};
+
+#define STM_USB_BDT_COUNT_RX_BL_SIZE 15
+#define STM_USB_BDT_COUNT_RX_NUM_BLOCK 10
+#define STM_USB_BDT_COUNT_RX_NUM_BLOCK_MASK 0x1fUL
+#define STM_USB_BDT_COUNT_RX_COUNT_RX 0
+#define STM_USB_BDT_COUNT_RX_COUNT_RX_MASK 0x3ffUL
+
+#define STM_USB_BDT_SIZE 8
+
+extern uint8_t stm_usb_sram[] __attribute__ ((aligned(4)));
+
+//#define stm_usb_sram ((uint8_t *)0x40006000)
+
+struct stm_dma_channel {
+ vuint32_t ccr;
+ vuint32_t cndtr;
+ vvoid_t cpar;
+ vvoid_t cmar;
+ vuint32_t reserved;
+};
+
+#define STM_NUM_DMA 7
+
+struct stm_dma {
+ vuint32_t isr;
+ vuint32_t ifcr;
+ struct stm_dma_channel channel[STM_NUM_DMA];
+};
+
+extern struct stm_dma stm_dma;
+
+#define stm_dma (*((struct stm_dma *) 0x40020000))
+
+/* DMA channels go from 1 to 7, instead of 0 to 6 (sigh)
+ */
+
+#define STM_DMA_INDEX(channel) ((channel) - 1)
+
+#define STM_DMA_ISR(index) ((index) << 2)
+#define STM_DMA_ISR_MASK 0xfUL
+#define STM_DMA_ISR_TEIF 3
+#define STM_DMA_ISR_HTIF 2
+#define STM_DMA_ISR_TCIF 1
+#define STM_DMA_ISR_GIF 0
+
+#define STM_DMA_IFCR(index) ((index) << 2)
+#define STM_DMA_IFCR_MASK 0xfUL
+#define STM_DMA_IFCR_CTEIF 3
+#define STM_DMA_IFCR_CHTIF 2
+#define STM_DMA_IFCR_CTCIF 1
+#define STM_DMA_IFCR_CGIF 0
+
+#define STM_DMA_CCR_MEM2MEM (14)
+
+#define STM_DMA_CCR_PL (12)
+#define STM_DMA_CCR_PL_LOW (0)
+#define STM_DMA_CCR_PL_MEDIUM (1)
+#define STM_DMA_CCR_PL_HIGH (2)
+#define STM_DMA_CCR_PL_VERY_HIGH (3)
+#define STM_DMA_CCR_PL_MASK (3)
+
+#define STM_DMA_CCR_MSIZE (10)
+#define STM_DMA_CCR_MSIZE_8 (0)
+#define STM_DMA_CCR_MSIZE_16 (1)
+#define STM_DMA_CCR_MSIZE_32 (2)
+#define STM_DMA_CCR_MSIZE_MASK (3)
+
+#define STM_DMA_CCR_PSIZE (8)
+#define STM_DMA_CCR_PSIZE_8 (0)
+#define STM_DMA_CCR_PSIZE_16 (1)
+#define STM_DMA_CCR_PSIZE_32 (2)
+#define STM_DMA_CCR_PSIZE_MASK (3)
+
+#define STM_DMA_CCR_MINC (7)
+#define STM_DMA_CCR_PINC (6)
+#define STM_DMA_CCR_CIRC (5)
+#define STM_DMA_CCR_DIR (4)
+#define STM_DMA_CCR_DIR_PER_TO_MEM 0
+#define STM_DMA_CCR_DIR_MEM_TO_PER 1
+#define STM_DMA_CCR_TEIE (3)
+#define STM_DMA_CCR_HTIE (2)
+#define STM_DMA_CCR_TCIE (1)
+#define STM_DMA_CCR_EN (0)
+
+#define STM_DMA_CHANNEL_ADC1 1
+#define STM_DMA_CHANNEL_SPI1_RX 2
+#define STM_DMA_CHANNEL_SPI1_TX 3
+#define STM_DMA_CHANNEL_SPI2_RX 4
+#define STM_DMA_CHANNEL_SPI2_TX 5
+#define STM_DMA_CHANNEL_USART3_TX 2
+#define STM_DMA_CHANNEL_USART3_RX 3
+#define STM_DMA_CHANNEL_USART1_TX 4
+#define STM_DMA_CHANNEL_USART1_RX 5
+#define STM_DMA_CHANNEL_USART2_RX 6
+#define STM_DMA_CHANNEL_USART2_TX 7
+#define STM_DMA_CHANNEL_I2C2_TX 4
+#define STM_DMA_CHANNEL_I2C2_RX 5
+#define STM_DMA_CHANNEL_I2C1_TX 6
+#define STM_DMA_CHANNEL_I2C1_RX 7
+#define STM_DMA_CHANNEL_TIM1_CH1 2
+#define STM_DMA_CHANNEL_TIM1_CH4 4
+#define STM_DMA_CHANNEL_TIM1_TRIG 4
+#define STM_DMA_CHANNEL_TIM1_COM 4
+#define STM_DMA_CHANNEL_TIM1_UP 5
+#define STM_DMA_CHANNEL_TIM1_CH3 6
+#define STM_DMA_CHANNEL_TIM2_CH3 1
+#define STM_DMA_CHANNEL_TIM2_UP 2
+#define STM_DMA_CHANNEL_TIM2_CH1 5
+#define STM_DMA_CHANNEL_TIM2_CH2 7
+#define STM_DMA_CHANNEL_TIM2_CH4 7
+#define STM_DMA_CHANNEL_TIM3_CH3 2
+#define STM_DMA_CHANNEL_TIM3_CH4 3
+#define STM_DMA_CHANNEL_TIM3_UP 3
+#define STM_DMA_CHANNEL_TIM3_CH1 6
+#define STM_DMA_CHANNEL_TIM3_TRIG 6
+#define STM_DMA_CHANNEL_TIM4_CH1 1
+#define STM_DMA_CHANNEL_TIM4_CH2 4
+#define STM_DMA_CHANNEL_TIM4_CH3 5
+#define STM_DMA_CHANNEL_TIM4_UP 7
+
+/* high density, xl-density and connectivity devices also have dma2 */
+
+#define STM_DMA2_CHANNEL_ADC3 5
+#define STM_DMA2_CHANNEL_SPI3_RX 1
+#define STM_DMA2_CHANNEL_SPI3_TX 2
+#define STM_DMA2_CHANNEL_UART4_RX 3
+#define STM_DMA2_CHANNEL_UART4_TX 5
+#define STM_DMA2_CHANNEL_TIM5_CH4 1
+#define STM_DMA2_CHANNEL_TIM5_TRIG 1
+#define STM_DMA2_CHANNEL_TIM5_CH3 2
+#define STM_DMA2_CHANNEL_TIM5_UP 2
+#define STM_DMA2_CHANNEL_TIM5_CH2 4
+#define STM_DMA2_CHANNEL_TIM5_CH1 5
+#define STM_DMA2_CHANNEL_TIM6_UP 3
+#define STM_DMA2_CHANNEL_DAC_CHANNEL1 3
+#define STM_DMA2_CHANNEL_TIM7_UP 4
+#define STM_DMA2_CHANNEL_DAC_CHANNEL2 4
+#define STM_DMA2_CHANNEL_TIM8_CH3 1
+#define STM_DMA2_CHANNEL_TIM8_UP 1
+#define STM_DMA2_CHANNEL_TIM8_CH4 2
+#define STM_DMA2_CHANNEL_TIM8_TRIG 2
+#define STM_DMA2_CHANNEL_TIM8_COM 2
+#define STM_DMA2_CHANNEL_TIM8_CH1 3
+#define STM_DMA2_CHANNEL_TIM8_CH2 5
+
+struct stm_spi {
+ vuint32_t cr1;
+ vuint32_t cr2;
+ vuint32_t sr;
+ vuint32_t dr;
+
+ vuint32_t crcpr;
+ vuint32_t rxcrcr;
+ vuint32_t txcrcr;
+ vuint32_t i2scfgr;
+
+ vuint32_t i2spr;
+};
+
+extern struct stm_spi stm_spi1, stm_spi2;
+
+#define stm_spi1 (*((struct stm_spi *) 0x40013000))
+#define stm_spi2 (*((struct stm_spi *) 0x40003800))
+
+/* SPI channels go from 1 to 2, instead of 0 to 1 (sigh)
+ */
+
+#define STM_NUM_SPI 2
+
+#define STM_SPI_INDEX(channel) ((channel) - 1)
+
+#define STM_SPI_CR1_BIDIMODE 15
+#define STM_SPI_CR1_BIDIOE 14
+#define STM_SPI_CR1_CRCEN 13
+#define STM_SPI_CR1_CRCNEXT 12
+#define STM_SPI_CR1_DFF 11
+#define STM_SPI_CR1_RXONLY 10
+#define STM_SPI_CR1_SSM 9
+#define STM_SPI_CR1_SSI 8
+#define STM_SPI_CR1_LSBFIRST 7
+#define STM_SPI_CR1_SPE 6
+#define STM_SPI_CR1_BR 3
+#define STM_SPI_CR1_BR_PCLK_2 0
+#define STM_SPI_CR1_BR_PCLK_4 1
+#define STM_SPI_CR1_BR_PCLK_8 2
+#define STM_SPI_CR1_BR_PCLK_16 3
+#define STM_SPI_CR1_BR_PCLK_32 4
+#define STM_SPI_CR1_BR_PCLK_64 5
+#define STM_SPI_CR1_BR_PCLK_128 6
+#define STM_SPI_CR1_BR_PCLK_256 7
+#define STM_SPI_CR1_BR_MASK 7UL
+
+#define STM_SPI_CR1_MSTR 2
+#define STM_SPI_CR1_CPOL 1
+#define STM_SPI_CR1_CPHA 0
+
+#define STM_SPI_CR2_TXEIE 7
+#define STM_SPI_CR2_RXNEIE 6
+#define STM_SPI_CR2_ERRIE 5
+#define STM_SPI_CR2_SSOE 2
+#define STM_SPI_CR2_TXDMAEN 1
+#define STM_SPI_CR2_RXDMAEN 0
+
+#define STM_SPI_SR_FRE 8
+#define STM_SPI_SR_BSY 7
+#define STM_SPI_SR_OVR 6
+#define STM_SPI_SR_MODF 5
+#define STM_SPI_SR_CRCERR 4
+#define STM_SPI_SR_UDR 3
+#define STM_SPI_SR_CHSIDE 2
+#define STM_SPI_SR_TXE 1
+#define STM_SPI_SR_RXNE 0
+
+#define STM_NUM_I2C 2
+
+#define STM_I2C_INDEX(channel) ((channel) - 1)
+
+struct stm_i2c {
+ vuint32_t cr1;
+ vuint32_t cr2;
+ vuint32_t oar1;
+ vuint32_t oar2;
+ vuint32_t dr;
+ vuint32_t sr1;
+ vuint32_t sr2;
+ vuint32_t ccr;
+ vuint32_t trise;
+};
+
+extern struct stm_i2c stm_i2c1, stm_i2c2;
+
+#define stm_i2c1 (*((struct stm_i2c *) 0x40005400))
+#define stm_i2c2 (*((struct stm_i2c *) 0x40005800))
+
+#define STM_I2C_CR1_SWRST 15
+#define STM_I2C_CR1_ALERT 13
+#define STM_I2C_CR1_PEC 12
+#define STM_I2C_CR1_POS 11
+#define STM_I2C_CR1_ACK 10
+#define STM_I2C_CR1_STOP 9
+#define STM_I2C_CR1_START 8
+#define STM_I2C_CR1_NOSTRETCH 7
+#define STM_I2C_CR1_ENGC 6
+#define STM_I2C_CR1_ENPEC 5
+#define STM_I2C_CR1_ENARP 4
+#define STM_I2C_CR1_SMBTYPE 3
+#define STM_I2C_CR1_SMBUS 1
+#define STM_I2C_CR1_PE 0
+
+#define STM_I2C_CR2_LAST 12
+#define STM_I2C_CR2_DMAEN 11
+#define STM_I2C_CR2_ITBUFEN 10
+#define STM_I2C_CR2_ITEVTEN 9
+#define STM_I2C_CR2_ITERREN 8
+#define STM_I2C_CR2_FREQ 0
+#define STM_I2C_CR2_FREQ_MASK 0x3fUL
+
+#define STM_I2C_SR1_SMBALERT 15
+#define STM_I2C_SR1_TIMEOUT 14
+#define STM_I2C_SR1_PECERR 12
+#define STM_I2C_SR1_OVR 11
+#define STM_I2C_SR1_AF 10
+#define STM_I2C_SR1_ARLO 9
+#define STM_I2C_SR1_BERR 8
+#define STM_I2C_SR1_TXE 7
+#define STM_I2C_SR1_RXNE 6
+#define STM_I2C_SR1_STOPF 4
+#define STM_I2C_SR1_ADD10 3
+#define STM_I2C_SR1_BTF 2
+#define STM_I2C_SR1_ADDR 1
+#define STM_I2C_SR1_SB 0
+
+#define STM_I2C_SR2_PEC 8
+#define STM_I2C_SR2_PEC_MASK 0xff00UL
+#define STM_I2C_SR2_DUALF 7
+#define STM_I2C_SR2_SMBHOST 6
+#define STM_I2C_SR2_SMBDEFAULT 5
+#define STM_I2C_SR2_GENCALL 4
+#define STM_I2C_SR2_TRA 2
+#define STM_I2C_SR2_BUSY 1
+#define STM_I2C_SR2_MSL 0
+
+#define STM_I2C_CCR_FS 15
+#define STM_I2C_CCR_DUTY 14
+#define STM_I2C_CCR_CCR 0
+#define STM_I2C_CCR_MASK 0x7ffUL
+
+struct stm_adc {
+ vuint32_t sr;
+ vuint32_t cr1;
+ vuint32_t cr2;
+ vuint32_t smpr1;
+
+ vuint32_t smpr2;
+ vuint32_t jofr1;
+ vuint32_t jofr2;
+ vuint32_t jofr3;
+
+ vuint32_t jofr4;
+ vuint32_t htr;
+ vuint32_t ltr;
+ vuint32_t sqr1;
+
+ vuint32_t sqr2;
+ vuint32_t sqr3;
+ vuint32_t jsqr;
+ vuint32_t jdr1;
+
+ vuint32_t jdr2;
+ vuint32_t jdr3;
+ vuint32_t jdr4;
+ vuint32_t dr;
+};
+
+extern struct stm_adc stm_adc1;
+
+//#define stm_adc1 (*((struct stm_adc *) 0x40012400))
+
+#define STM_ADC_SQ_TEMP 16
+#define STM_ADC_SQ_V_REF 17
+
+#define STM_ADC_SR_STRT 4
+#define STM_ADC_SR_JSTRT 3
+#define STM_ADC_SR_JEOC 2
+#define STM_ADC_SR_EOC 1
+#define STM_ADC_SR_AWD 0
+
+#define STM_ADC_CR1_AWDEN 23
+#define STM_ADC_CR1_JAWDEN 22
+#define STM_ADC_CR1_DUALMOD 16
+# define STM_ADC_CR1_DUALMOD_INDEPENDENT 0
+# define STM_ADC_CR1_DUALMOD_COMB_REG_SIM_INJ_SIM 1
+# define STM_ADC_CR1_DUALMOD_COMB_REG_SIM_ALT_TRIG 2
+# define STM_ADC_CR1_DUALMOD_COMB_INJ_SIM_FAST_INT 3
+# define STM_ADC_CR1_DUALMOD_COMB_INJ_SIM_SLOW_INT 4
+# define STM_ADC_CR1_DUALMOD_INJ_SIM 5
+# define STM_ADC_CR1_DUALMOD_REG_SIM 6
+# define STM_ADC_CR1_DUALMOD_FAST_INT 7
+# define STM_ADC_CR1_DUALMOD_SLOW_INT 8
+# define STM_ADC_CR1_DUALMOD_ALT_TRIG 9
+
+#define STM_ADC_CR1_DISCNUM 13
+#define STM_ADC_CR1_DISCNUM_1 0
+#define STM_ADC_CR1_DISCNUM_2 1
+#define STM_ADC_CR1_DISCNUM_3 2
+#define STM_ADC_CR1_DISCNUM_4 3
+#define STM_ADC_CR1_DISCNUM_5 4
+#define STM_ADC_CR1_DISCNUM_6 5
+#define STM_ADC_CR1_DISCNUM_7 6
+#define STM_ADC_CR1_DISCNUM_8 7
+#define STM_ADC_CR1_DISCNUM_MASK 7UL
+#define STM_ADC_CR1_JDISCEN 12
+#define STM_ADC_CR1_DISCEN 11
+#define STM_ADC_CR1_JAUTO 10
+#define STM_ADC_CR1_AWDSGL 9
+#define STM_ADC_CR1_SCAN 8
+#define STM_ADC_CR1_JEOCIE 7
+#define STM_ADC_CR1_AWDIE 6
+#define STM_ADC_CR1_EOCIE 5
+#define STM_ADC_CR1_AWDCH 0
+#define STM_ADC_CR1_AWDCH_MASK 0x1fUL
+
+#define STM_ADC_CR2_TSVREFE 23
+#define STM_ADC_CR2_SWSTART 22
+#define STM_ADC_CR2_JWSTART 21
+#define STM_ADC_CR2_EXTTRIG 20
+#define STM_ADC_CR2_EXTSEL 17
+#define STM_ADC_CR2_EXTSEL_TIM1_CC1 0
+#define STM_ADC_CR2_EXTSEL_TIM1_CC2 1
+#define STM_ADC_CR2_EXTSEL_TIM1_CC3 2
+#define STM_ADC_CR2_EXTSEL_TIM2_CC2 3
+#define STM_ADC_CR2_EXTSEL_TIM3_TRGO 4
+#define STM_ADC_CR2_EXTSEL_TIM4_CC4 5
+#define STM_ADC_CR2_EXTSEL_EXTI 6
+#define STM_ADC_CR2_EXTSEL_SWSTART 7
+#define STM_ADC_CR2_EXTSEL_MASK 7UL
+#define STM_ADC_CR2_JEXTTRIG 15
+#define STM_ADC_CR2_JEXTSEL 12
+#define STM_ADC_CR2_JEXTSEL_TIM1_TRGO 0
+#define STM_ADC_CR2_JEXTSEL_TIM1_CC4 1
+#define STM_ADC_CR2_JEXTSEL_TIM2_TRGO 2
+#define STM_ADC_CR2_JEXTSEL_TIM2_CC1 3
+#define STM_ADC_CR2_JEXTSEL_TIM3_CC4 4
+#define STM_ADC_CR2_JEXTSEL_TIM4_TRGO 5
+#define STM_ADC_CR2_JEXTSEL_EXTI_15 6
+#define STM_ADC_CR2_JEXTSEL_JSWSTART 7
+#define STM_ADC_CR2_JEXTSEL_MASK 7UL
+#define STM_ADC_CR2_ALIGN 11
+#define STM_ADC_CR2_DMA 8
+#define STM_ADC_CR2_RSTCAL 3
+#define STM_ADC_CR2_CAL 2
+#define STM_ADC_CR2_CONT 1
+#define STM_ADC_CR2_ADON 0
+
+struct stm_exti {
+ vuint32_t imr;
+ vuint32_t emr;
+ vuint32_t rtsr;
+ vuint32_t ftsr;
+
+ vuint32_t swier;
+ vuint32_t pr;
+};
+
+extern struct stm_exti stm_exti;
+
+#define stm_exti (*((struct stm_exti *) 0x40010400))
+
+static inline void
+stm_exticr_set(struct stm_gpio *gpio, int pin) {
+ uint8_t reg = (uint8_t) (pin >> 2);
+ uint8_t shift = (pin & 3) << 2;
+ uint8_t val = 0;
+
+ if (gpio == &stm_gpioa)
+ val = STM_AFIO_EXTICR_PA;
+ else if (gpio == &stm_gpiob)
+ val = STM_AFIO_EXTICR_PB;
+ else if (gpio == &stm_gpioc)
+ val = STM_AFIO_EXTICR_PC;
+ else if (gpio == &stm_gpiod)
+ val = STM_AFIO_EXTICR_PD;
+ else if (gpio == &stm_gpioe)
+ val = STM_AFIO_EXTICR_PE;
+
+ stm_afio.exticr[reg] = (stm_afio.exticr[reg] & (uint32_t) ~(0xf << shift)) | val << shift;
+}
+
+struct stm_tim18 {
+ vuint32_t cr1;
+ vuint32_t cr2;
+ vuint32_t smcr;
+ vuint32_t dier;
+
+ vuint32_t sr;
+ vuint32_t egr;
+ vuint32_t ccmr1;
+ vuint32_t ccmr2;
+
+ vuint32_t ccer;
+ vuint32_t cnt;
+ vuint32_t psc;
+ vuint32_t arr;
+
+ vuint32_t rcr;
+ vuint32_t ccr1;
+ vuint32_t ccr2;
+ vuint32_t ccr3;
+
+ vuint32_t ccr4;
+ uint32_t bdtr;
+ vuint32_t dcr;
+ vuint32_t dmar;
+};
+
+extern struct stm_tim18 stm_tim1, stm_tim8;
+
+#define stm_tim1 (*((struct stm_tim18 *) 0x40012c00))
+#define stm_tim8 (*((struct stm_tim18 *) 0x40013400))
+
+#define STM_TIM18_CR1_CKD 8
+#define STM_TIM18_CR1_CKD_1 0
+#define STM_TIM18_CR1_CKD_2 1
+#define STM_TIM18_CR1_CKD_4 2
+#define STM_TIM18_CR1_CKD_MASK 3UL
+#define STM_TIM18_CR1_ARPE 7
+#define STM_TIM18_CR1_CMS 5
+#define STM_TIM18_CR1_CMS_EDGE 0
+#define STM_TIM18_CR1_CMS_CENTER_1 1
+#define STM_TIM18_CR1_CMS_CENTER_2 2
+#define STM_TIM18_CR1_CMS_CENTER_3 3
+#define STM_TIM18_CR1_CMS_MASK 3UL
+#define STM_TIM18_CR1_DIR 4
+#define STM_TIM18_CR1_DIR_UP 0
+#define STM_TIM18_CR1_DIR_DOWN 1
+#define STM_TIM18_CR1_OPM 3
+#define STM_TIM18_CR1_URS 2
+#define STM_TIM18_CR1_UDIS 1
+#define STM_TIM18_CR1_CEN 0
+
+#define STM_TIM18_CR2_TI1S 7
+#define STM_TIM18_CR2_MMS 4
+#define STM_TIM18_CR2_MMS_RESET 0
+#define STM_TIM18_CR2_MMS_ENABLE 1
+#define STM_TIM18_CR2_MMS_UPDATE 2
+#define STM_TIM18_CR2_MMS_COMPARE_PULSE 3
+#define STM_TIM18_CR2_MMS_COMPARE_OC1REF 4
+#define STM_TIM18_CR2_MMS_COMPARE_OC2REF 5
+#define STM_TIM18_CR2_MMS_COMPARE_OC3REF 6
+#define STM_TIM18_CR2_MMS_COMPARE_OC4REF 7
+#define STM_TIM18_CR2_MMS_MASK 7UL
+#define STM_TIM18_CR2_CCDS 3
+
+#define STM_TIM18_SMCR_ETP 15
+#define STM_TIM18_SMCR_ECE 14
+#define STM_TIM18_SMCR_ETPS 12
+#define STM_TIM18_SMCR_ETPS_OFF 0
+#define STM_TIM18_SMCR_ETPS_DIV_2 1
+#define STM_TIM18_SMCR_ETPS_DIV_4 2
+#define STM_TIM18_SMCR_ETPS_DIV_8 3
+#define STM_TIM18_SMCR_ETPS_MASK 3UL
+#define STM_TIM18_SMCR_ETF 8
+#define STM_TIM18_SMCR_ETF_NONE 0
+#define STM_TIM18_SMCR_ETF_INT_N_2 1
+#define STM_TIM18_SMCR_ETF_INT_N_4 2
+#define STM_TIM18_SMCR_ETF_INT_N_8 3
+#define STM_TIM18_SMCR_ETF_DTS_2_N_6 4
+#define STM_TIM18_SMCR_ETF_DTS_2_N_8 5
+#define STM_TIM18_SMCR_ETF_DTS_4_N_6 6
+#define STM_TIM18_SMCR_ETF_DTS_4_N_8 7
+#define STM_TIM18_SMCR_ETF_DTS_8_N_6 8
+#define STM_TIM18_SMCR_ETF_DTS_8_N_8 9
+#define STM_TIM18_SMCR_ETF_DTS_16_N_5 10
+#define STM_TIM18_SMCR_ETF_DTS_16_N_6 11
+#define STM_TIM18_SMCR_ETF_DTS_16_N_8 12
+#define STM_TIM18_SMCR_ETF_DTS_32_N_5 13
+#define STM_TIM18_SMCR_ETF_DTS_32_N_6 14
+#define STM_TIM18_SMCR_ETF_DTS_32_N_8 15
+#define STM_TIM18_SMCR_ETF_MASK 15UL
+#define STM_TIM18_SMCR_MSM 7
+#define STM_TIM18_SMCR_TS 4
+#define STM_TIM18_SMCR_TS_ITR0 0
+#define STM_TIM18_SMCR_TS_ITR1 1
+#define STM_TIM18_SMCR_TS_ITR2 2
+#define STM_TIM18_SMCR_TS_ITR3 3
+#define STM_TIM18_SMCR_TS_TI1F_ED 4
+#define STM_TIM18_SMCR_TS_TI1FP1 5
+#define STM_TIM18_SMCR_TS_TI2FP2 6
+#define STM_TIM18_SMCR_TS_ETRF 7
+#define STM_TIM18_SMCR_TS_MASK 7UL
+#define STM_TIM18_SMCR_SMS 0
+#define STM_TIM18_SMCR_SMS_DISABLE 0
+#define STM_TIM18_SMCR_SMS_ENCODER_MODE_1 1
+#define STM_TIM18_SMCR_SMS_ENCODER_MODE_2 2
+#define STM_TIM18_SMCR_SMS_ENCODER_MODE_3 3
+#define STM_TIM18_SMCR_SMS_RESET_MODE 4
+#define STM_TIM18_SMCR_SMS_GATED_MODE 5
+#define STM_TIM18_SMCR_SMS_TRIGGER_MODE 6
+#define STM_TIM18_SMCR_SMS_EXTERNAL_CLOCK 7
+#define STM_TIM18_SMCR_SMS_MASK 7UL
+
+#define STM_TIM18_DIER_TDE 14
+#define STM_TIM18_DIER_CC4DE 12
+#define STM_TIM18_DIER_CC3DE 11
+#define STM_TIM18_DIER_CC2DE 10
+#define STM_TIM18_DIER_CC1DE 9
+#define STM_TIM18_DIER_UDE 8
+
+#define STM_TIM18_DIER_TIE 6
+#define STM_TIM18_DIER_CC4IE 4
+#define STM_TIM18_DIER_CC3IE 3
+#define STM_TIM18_DIER_CC2IE 2
+#define STM_TIM18_DIER_CC1IE 1
+#define STM_TIM18_DIER_UIE 0
+
+#define STM_TIM18_SR_CC4OF 12
+#define STM_TIM18_SR_CC3OF 11
+#define STM_TIM18_SR_CC2OF 10
+#define STM_TIM18_SR_CC1OF 9
+#define STM_TIM18_SR_TIF 6
+#define STM_TIM18_SR_CC4IF 4
+#define STM_TIM18_SR_CC3IF 3
+#define STM_TIM18_SR_CC2IF 2
+#define STM_TIM18_SR_CC1IF 1
+#define STM_TIM18_SR_UIF 0
+
+#define STM_TIM18_EGR_TG 6
+#define STM_TIM18_EGR_CC4G 4
+#define STM_TIM18_EGR_CC3G 3
+#define STM_TIM18_EGR_CC2G 2
+#define STM_TIM18_EGR_CC1G 1
+#define STM_TIM18_EGR_UG 0
+
+#define STM_TIM18_CCMR1_OC2CE 15
+#define STM_TIM18_CCMR1_OC2M 12
+#define STM_TIM18_CCMR1_OC2M_FROZEN 0
+#define STM_TIM18_CCMR1_OC2M_SET_HIGH_ON_MATCH 1
+#define STM_TIM18_CCMR1_OC2M_SET_LOW_ON_MATCH 2
+#define STM_TIM18_CCMR1_OC2M_TOGGLE 3
+#define STM_TIM18_CCMR1_OC2M_FORCE_LOW 4
+#define STM_TIM18_CCMR1_OC2M_FORCE_HIGH 5
+#define STM_TIM18_CCMR1_OC2M_PWM_MODE_1 6
+#define STM_TIM18_CCMR1_OC2M_PWM_MODE_2 7
+#define STM_TIM18_CCMR1_OC2M_MASK 7UL
+#define STM_TIM18_CCMR1_OC2PE 11
+#define STM_TIM18_CCMR1_OC2FE 10
+#define STM_TIM18_CCMR1_CC2S 8
+#define STM_TIM18_CCMR1_CC2S_OUTPUT 0
+#define STM_TIM18_CCMR1_CC2S_INPUT_TI2 1
+#define STM_TIM18_CCMR1_CC2S_INPUT_TI1 2
+#define STM_TIM18_CCMR1_CC2S_INPUT_TRC 3
+#define STM_TIM18_CCMR1_CC2S_MASK 3UL
+
+#define STM_TIM18_CCMR1_OC1CE 7
+#define STM_TIM18_CCMR1_OC1M 4
+#define STM_TIM18_CCMR1_OC1M_FROZEN 0
+#define STM_TIM18_CCMR1_OC1M_SET_HIGH_ON_MATCH 1
+#define STM_TIM18_CCMR1_OC1M_SET_LOW_ON_MATCH 2
+#define STM_TIM18_CCMR1_OC1M_TOGGLE 3
+#define STM_TIM18_CCMR1_OC1M_FORCE_LOW 4
+#define STM_TIM18_CCMR1_OC1M_FORCE_HIGH 5
+#define STM_TIM18_CCMR1_OC1M_PWM_MODE_1 6
+#define STM_TIM18_CCMR1_OC1M_PWM_MODE_2 7
+#define STM_TIM18_CCMR1_OC1M_MASK 7UL
+#define STM_TIM18_CCMR1_OC1PE 3
+#define STM_TIM18_CCMR1_OC1FE 2
+#define STM_TIM18_CCMR1_CC1S 0
+#define STM_TIM18_CCMR1_CC1S_OUTPUT 0
+#define STM_TIM18_CCMR1_CC1S_INPUT_TI1 1
+#define STM_TIM18_CCMR1_CC1S_INPUT_TI2 2
+#define STM_TIM18_CCMR1_CC1S_INPUT_TRC 3
+#define STM_TIM18_CCMR1_CC1S_MASK 3UL
+
+#define STM_TIM18_CCMR1_IC2F 12
+#define STM_TIM18_CCMR1_IC2F_NONE 0
+#define STM_TIM18_CCMR1_IC2F_CK_INT_N_2 1
+#define STM_TIM18_CCMR1_IC2F_CK_INT_N_4 2
+#define STM_TIM18_CCMR1_IC2F_CK_INT_N_8 3
+#define STM_TIM18_CCMR1_IC2F_DTS_2_N_6 4
+#define STM_TIM18_CCMR1_IC2F_DTS_2_N_8 5
+#define STM_TIM18_CCMR1_IC2F_DTS_4_N_6 6
+#define STM_TIM18_CCMR1_IC2F_DTS_4_N_8 7
+#define STM_TIM18_CCMR1_IC2F_DTS_8_N_6 8
+#define STM_TIM18_CCMR1_IC2F_DTS_8_N_8 9
+#define STM_TIM18_CCMR1_IC2F_DTS_16_N_5 10
+#define STM_TIM18_CCMR1_IC2F_DTS_16_N_6 11
+#define STM_TIM18_CCMR1_IC2F_DTS_16_N_8 12
+#define STM_TIM18_CCMR1_IC2F_DTS_32_N_5 13
+#define STM_TIM18_CCMR1_IC2F_DTS_32_N_6 14
+#define STM_TIM18_CCMR1_IC2F_DTS_32_N_8 15
+#define STM_TIM18_CCMR1_IC2PSC 10
+#define STM_TIM18_CCMR1_IC2PSC_NONE 0
+#define STM_TIM18_CCMR1_IC2PSC_2 1
+#define STM_TIM18_CCMR1_IC2PSC_4 2
+#define STM_TIM18_CCMR1_IC2PSC_8 3
+#define STM_TIM18_CCMR1_IC1F 4
+#define STM_TIM18_CCMR1_IC1F_NONE 0
+#define STM_TIM18_CCMR1_IC1F_CK_INT_N_2 1
+#define STM_TIM18_CCMR1_IC1F_CK_INT_N_4 2
+#define STM_TIM18_CCMR1_IC1F_CK_INT_N_8 3
+#define STM_TIM18_CCMR1_IC1F_DTS_2_N_6 4
+#define STM_TIM18_CCMR1_IC1F_DTS_2_N_8 5
+#define STM_TIM18_CCMR1_IC1F_DTS_4_N_6 6
+#define STM_TIM18_CCMR1_IC1F_DTS_4_N_8 7
+#define STM_TIM18_CCMR1_IC1F_DTS_8_N_6 8
+#define STM_TIM18_CCMR1_IC1F_DTS_8_N_8 9
+#define STM_TIM18_CCMR1_IC1F_DTS_16_N_5 10
+#define STM_TIM18_CCMR1_IC1F_DTS_16_N_6 11
+#define STM_TIM18_CCMR1_IC1F_DTS_16_N_8 12
+#define STM_TIM18_CCMR1_IC1F_DTS_32_N_5 13
+#define STM_TIM18_CCMR1_IC1F_DTS_32_N_6 14
+#define STM_TIM18_CCMR1_IC1F_DTS_32_N_8 15
+#define STM_TIM18_CCMR1_IC1PSC 2
+#define STM_TIM18_CCMR1_IC1PSC_NONE 0
+#define STM_TIM18_CCMR1_IC1PSC_2 1
+#define STM_TIM18_CCMR1_IC1PSC_4 2
+#define STM_TIM18_CCMR1_IC1PSC_8 3
+
+#define STM_TIM18_CCMR2_OC4CE 15
+#define STM_TIM18_CCMR2_OC4M 12
+#define STM_TIM18_CCMR2_OC4M_FROZEN 0
+#define STM_TIM18_CCMR2_OC4M_SET_HIGH_ON_MATCH 1
+#define STM_TIM18_CCMR2_OC4M_SET_LOW_ON_MATCH 2
+#define STM_TIM18_CCMR2_OC4M_TOGGLE 3
+#define STM_TIM18_CCMR2_OC4M_FORCE_LOW 4
+#define STM_TIM18_CCMR2_OC4M_FORCE_HIGH 5
+#define STM_TIM18_CCMR2_OC4M_PWM_MODE_1 6
+#define STM_TIM18_CCMR2_OC4M_PWM_MODE_2 7
+#define STM_TIM18_CCMR2_OC4M_MASK 7UL
+#define STM_TIM18_CCMR2_OC4PE 11
+#define STM_TIM18_CCMR2_OC4FE 10
+#define STM_TIM18_CCMR2_CC4S 8
+#define STM_TIM18_CCMR2_CC4S_OUTPUT 0
+#define STM_TIM18_CCMR2_CC4S_INPUT_TI4 1
+#define STM_TIM18_CCMR2_CC4S_INPUT_TI3 2
+#define STM_TIM18_CCMR2_CC4S_INPUT_TRC 3
+#define STM_TIM18_CCMR2_CC4S_MASK 3UL
+
+#define STM_TIM18_CCMR2_OC3CE 7
+#define STM_TIM18_CCMR2_OC3M 4
+#define STM_TIM18_CCMR2_OC3M_FROZEN 0
+#define STM_TIM18_CCMR2_OC3M_SET_HIGH_ON_MATCH 1
+#define STM_TIM18_CCMR2_OC3M_SET_LOW_ON_MATCH 2
+#define STM_TIM18_CCMR2_OC3M_TOGGLE 3
+#define STM_TIM18_CCMR2_OC3M_FORCE_LOW 4
+#define STM_TIM18_CCMR2_OC3M_FORCE_HIGH 5
+#define STM_TIM18_CCMR2_OC3M_PWM_MODE_1 6
+#define STM_TIM18_CCMR2_OC3M_PWM_MODE_2 7
+#define STM_TIM18_CCMR2_OC3M_MASK 7UL
+#define STM_TIM18_CCMR2_OC3PE 3
+#define STM_TIM18_CCMR2_OC3FE 2
+#define STM_TIM18_CCMR2_CC3S 0
+#define STM_TIM18_CCMR2_CC3S_OUTPUT 0
+#define STM_TIM18_CCMR2_CC3S_INPUT_TI3 1
+#define STM_TIM18_CCMR2_CC3S_INPUT_TI4 2
+#define STM_TIM18_CCMR2_CC3S_INPUT_TRC 3
+#define STM_TIM18_CCMR2_CC3S_MASK 3UL
+
+#define STM_TIM18_CCER_CC4NP 15
+#define STM_TIM18_CCER_CC4P 13
+#define STM_TIM18_CCER_CC4P_ACTIVE_HIGH 0
+#define STM_TIM18_CCER_CC4P_ACTIVE_LOW 1
+#define STM_TIM18_CCER_CC4E 12
+#define STM_TIM18_CCER_CC3NP 11
+#define STM_TIM18_CCER_CC3P 9
+#define STM_TIM18_CCER_CC3P_ACTIVE_HIGH 0
+#define STM_TIM18_CCER_CC3P_ACTIVE_LOW 1
+#define STM_TIM18_CCER_CC3E 8
+#define STM_TIM18_CCER_CC2NP 7
+#define STM_TIM18_CCER_CC2P 5
+#define STM_TIM18_CCER_CC2P_ACTIVE_HIGH 0
+#define STM_TIM18_CCER_CC2P_ACTIVE_LOW 1
+#define STM_TIM18_CCER_CC2E 4
+#define STM_TIM18_CCER_CC1NP 3
+#define STM_TIM18_CCER_CC1P 1
+#define STM_TIM18_CCER_CC1P_ACTIVE_HIGH 0
+#define STM_TIM18_CCER_CC1P_ACTIVE_LOW 1
+#define STM_TIM18_CCER_CC1E 0
+
+struct stm_tim234 {
+ vuint32_t cr1;
+ vuint32_t cr2;
+ vuint32_t smcr;
+ vuint32_t dier;
+
+ vuint32_t sr;
+ vuint32_t egr;
+ vuint32_t ccmr1;
+ vuint32_t ccmr2;
+
+ vuint32_t ccer;
+ vuint32_t cnt;
+ vuint32_t psc;
+ vuint32_t arr;
+
+ uint32_t reserved_30;
+ vuint32_t ccr1;
+ vuint32_t ccr2;
+ vuint32_t ccr3;
+
+ vuint32_t ccr4;
+ uint32_t reserved_44;
+ vuint32_t dcr;
+ vuint32_t dmar;
+};
+
+extern struct stm_tim234 stm_tim2, stm_tim3, stm_tim4;
+
+#define stm_tim2 (*((struct stm_tim234 *) 0x40000000))
+#define stm_tim3 (*((struct stm_tim234 *) 0x40000400))
+#define stm_tim4 (*((struct stm_tim234 *) 0x40000800))
+
+#define STM_TIM234_CR1_CKD 8
+#define STM_TIM234_CR1_CKD_1 0
+#define STM_TIM234_CR1_CKD_2 1
+#define STM_TIM234_CR1_CKD_4 2
+#define STM_TIM234_CR1_CKD_MASK 3UL
+#define STM_TIM234_CR1_ARPE 7
+#define STM_TIM234_CR1_CMS 5
+#define STM_TIM234_CR1_CMS_EDGE 0
+#define STM_TIM234_CR1_CMS_CENTER_1 1
+#define STM_TIM234_CR1_CMS_CENTER_2 2
+#define STM_TIM234_CR1_CMS_CENTER_3 3
+#define STM_TIM234_CR1_CMS_MASK 3UL
+#define STM_TIM234_CR1_DIR 4
+#define STM_TIM234_CR1_DIR_UP 0
+#define STM_TIM234_CR1_DIR_DOWN 1
+#define STM_TIM234_CR1_OPM 3
+#define STM_TIM234_CR1_URS 2
+#define STM_TIM234_CR1_UDIS 1
+#define STM_TIM234_CR1_CEN 0
+
+#define STM_TIM234_CR2_TI1S 7
+#define STM_TIM234_CR2_MMS 4
+#define STM_TIM234_CR2_MMS_RESET 0
+#define STM_TIM234_CR2_MMS_ENABLE 1
+#define STM_TIM234_CR2_MMS_UPDATE 2
+#define STM_TIM234_CR2_MMS_COMPARE_PULSE 3
+#define STM_TIM234_CR2_MMS_COMPARE_OC1REF 4
+#define STM_TIM234_CR2_MMS_COMPARE_OC2REF 5
+#define STM_TIM234_CR2_MMS_COMPARE_OC3REF 6
+#define STM_TIM234_CR2_MMS_COMPARE_OC4REF 7
+#define STM_TIM234_CR2_MMS_MASK 7UL
+#define STM_TIM234_CR2_CCDS 3
+
+#define STM_TIM234_SMCR_ETP 15
+#define STM_TIM234_SMCR_ECE 14
+#define STM_TIM234_SMCR_ETPS 12
+#define STM_TIM234_SMCR_ETPS_OFF 0
+#define STM_TIM234_SMCR_ETPS_DIV_2 1
+#define STM_TIM234_SMCR_ETPS_DIV_4 2
+#define STM_TIM234_SMCR_ETPS_DIV_8 3
+#define STM_TIM234_SMCR_ETPS_MASK 3UL
+#define STM_TIM234_SMCR_ETF 8
+#define STM_TIM234_SMCR_ETF_NONE 0
+#define STM_TIM234_SMCR_ETF_INT_N_2 1
+#define STM_TIM234_SMCR_ETF_INT_N_4 2
+#define STM_TIM234_SMCR_ETF_INT_N_8 3
+#define STM_TIM234_SMCR_ETF_DTS_2_N_6 4
+#define STM_TIM234_SMCR_ETF_DTS_2_N_8 5
+#define STM_TIM234_SMCR_ETF_DTS_4_N_6 6
+#define STM_TIM234_SMCR_ETF_DTS_4_N_8 7
+#define STM_TIM234_SMCR_ETF_DTS_8_N_6 8
+#define STM_TIM234_SMCR_ETF_DTS_8_N_8 9
+#define STM_TIM234_SMCR_ETF_DTS_16_N_5 10
+#define STM_TIM234_SMCR_ETF_DTS_16_N_6 11
+#define STM_TIM234_SMCR_ETF_DTS_16_N_8 12
+#define STM_TIM234_SMCR_ETF_DTS_32_N_5 13
+#define STM_TIM234_SMCR_ETF_DTS_32_N_6 14
+#define STM_TIM234_SMCR_ETF_DTS_32_N_8 15
+#define STM_TIM234_SMCR_ETF_MASK 15UL
+#define STM_TIM234_SMCR_MSM 7
+#define STM_TIM234_SMCR_TS 4
+#define STM_TIM234_SMCR_TS_ITR0 0
+#define STM_TIM234_SMCR_TS_ITR1 1
+#define STM_TIM234_SMCR_TS_ITR2 2
+#define STM_TIM234_SMCR_TS_ITR3 3
+#define STM_TIM234_SMCR_TS_TI1F_ED 4
+#define STM_TIM234_SMCR_TS_TI1FP1 5
+#define STM_TIM234_SMCR_TS_TI2FP2 6
+#define STM_TIM234_SMCR_TS_ETRF 7
+#define STM_TIM234_SMCR_TS_MASK 7UL
+#define STM_TIM234_SMCR_SMS 0
+#define STM_TIM234_SMCR_SMS_DISABLE 0
+#define STM_TIM234_SMCR_SMS_ENCODER_MODE_1 1
+#define STM_TIM234_SMCR_SMS_ENCODER_MODE_2 2
+#define STM_TIM234_SMCR_SMS_ENCODER_MODE_3 3
+#define STM_TIM234_SMCR_SMS_RESET_MODE 4
+#define STM_TIM234_SMCR_SMS_GATED_MODE 5
+#define STM_TIM234_SMCR_SMS_TRIGGER_MODE 6
+#define STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK 7
+#define STM_TIM234_SMCR_SMS_MASK 7UL
+
+#define STM_TIM234_DIER_TDE 14
+#define STM_TIM234_DIER_CC4DE 12
+#define STM_TIM234_DIER_CC3DE 11
+#define STM_TIM234_DIER_CC2DE 10
+#define STM_TIM234_DIER_CC1DE 9
+#define STM_TIM234_DIER_UDE 8
+
+#define STM_TIM234_DIER_TIE 6
+#define STM_TIM234_DIER_CC4IE 4
+#define STM_TIM234_DIER_CC3IE 3
+#define STM_TIM234_DIER_CC2IE 2
+#define STM_TIM234_DIER_CC1IE 1
+#define STM_TIM234_DIER_UIE 0
+
+#define STM_TIM234_SR_CC4OF 12
+#define STM_TIM234_SR_CC3OF 11
+#define STM_TIM234_SR_CC2OF 10
+#define STM_TIM234_SR_CC1OF 9
+#define STM_TIM234_SR_TIF 6
+#define STM_TIM234_SR_CC4IF 4
+#define STM_TIM234_SR_CC3IF 3
+#define STM_TIM234_SR_CC2IF 2
+#define STM_TIM234_SR_CC1IF 1
+#define STM_TIM234_SR_UIF 0
+
+#define STM_TIM234_EGR_TG 6
+#define STM_TIM234_EGR_CC4G 4
+#define STM_TIM234_EGR_CC3G 3
+#define STM_TIM234_EGR_CC2G 2
+#define STM_TIM234_EGR_CC1G 1
+#define STM_TIM234_EGR_UG 0
+
+#define STM_TIM234_CCMR1_OC2CE 15
+#define STM_TIM234_CCMR1_OC2M 12
+#define STM_TIM234_CCMR1_OC2M_FROZEN 0
+#define STM_TIM234_CCMR1_OC2M_SET_HIGH_ON_MATCH 1
+#define STM_TIM234_CCMR1_OC2M_SET_LOW_ON_MATCH 2
+#define STM_TIM234_CCMR1_OC2M_TOGGLE 3
+#define STM_TIM234_CCMR1_OC2M_FORCE_LOW 4
+#define STM_TIM234_CCMR1_OC2M_FORCE_HIGH 5
+#define STM_TIM234_CCMR1_OC2M_PWM_MODE_1 6
+#define STM_TIM234_CCMR1_OC2M_PWM_MODE_2 7
+#define STM_TIM234_CCMR1_OC2M_MASK 7UL
+#define STM_TIM234_CCMR1_OC2PE 11
+#define STM_TIM234_CCMR1_OC2FE 10
+#define STM_TIM234_CCMR1_CC2S 8
+#define STM_TIM234_CCMR1_CC2S_OUTPUT 0
+#define STM_TIM234_CCMR1_CC2S_INPUT_TI2 1
+#define STM_TIM234_CCMR1_CC2S_INPUT_TI1 2
+#define STM_TIM234_CCMR1_CC2S_INPUT_TRC 3
+#define STM_TIM234_CCMR1_CC2S_MASK 3UL
+
+#define STM_TIM234_CCMR1_OC1CE 7
+#define STM_TIM234_CCMR1_OC1M 4
+#define STM_TIM234_CCMR1_OC1M_FROZEN 0
+#define STM_TIM234_CCMR1_OC1M_SET_HIGH_ON_MATCH 1
+#define STM_TIM234_CCMR1_OC1M_SET_LOW_ON_MATCH 2
+#define STM_TIM234_CCMR1_OC1M_TOGGLE 3
+#define STM_TIM234_CCMR1_OC1M_FORCE_LOW 4
+#define STM_TIM234_CCMR1_OC1M_FORCE_HIGH 5
+#define STM_TIM234_CCMR1_OC1M_PWM_MODE_1 6
+#define STM_TIM234_CCMR1_OC1M_PWM_MODE_2 7
+#define STM_TIM234_CCMR1_OC1M_MASK 7UL
+#define STM_TIM234_CCMR1_OC1PE 3
+#define STM_TIM234_CCMR1_OC1FE 2
+#define STM_TIM234_CCMR1_CC1S 0
+#define STM_TIM234_CCMR1_CC1S_OUTPUT 0
+#define STM_TIM234_CCMR1_CC1S_INPUT_TI1 1
+#define STM_TIM234_CCMR1_CC1S_INPUT_TI2 2
+#define STM_TIM234_CCMR1_CC1S_INPUT_TRC 3
+#define STM_TIM234_CCMR1_CC1S_MASK 3UL
+
+#define STM_TIM234_CCMR1_IC2F 12
+#define STM_TIM234_CCMR1_IC2F_NONE 0
+#define STM_TIM234_CCMR1_IC2F_CK_INT_N_2 1
+#define STM_TIM234_CCMR1_IC2F_CK_INT_N_4 2
+#define STM_TIM234_CCMR1_IC2F_CK_INT_N_8 3
+#define STM_TIM234_CCMR1_IC2F_DTS_2_N_6 4
+#define STM_TIM234_CCMR1_IC2F_DTS_2_N_8 5
+#define STM_TIM234_CCMR1_IC2F_DTS_4_N_6 6
+#define STM_TIM234_CCMR1_IC2F_DTS_4_N_8 7
+#define STM_TIM234_CCMR1_IC2F_DTS_8_N_6 8
+#define STM_TIM234_CCMR1_IC2F_DTS_8_N_8 9
+#define STM_TIM234_CCMR1_IC2F_DTS_16_N_5 10
+#define STM_TIM234_CCMR1_IC2F_DTS_16_N_6 11
+#define STM_TIM234_CCMR1_IC2F_DTS_16_N_8 12
+#define STM_TIM234_CCMR1_IC2F_DTS_32_N_5 13
+#define STM_TIM234_CCMR1_IC2F_DTS_32_N_6 14
+#define STM_TIM234_CCMR1_IC2F_DTS_32_N_8 15
+#define STM_TIM234_CCMR1_IC2PSC 10
+#define STM_TIM234_CCMR1_IC2PSC_NONE 0
+#define STM_TIM234_CCMR1_IC2PSC_2 1
+#define STM_TIM234_CCMR1_IC2PSC_4 2
+#define STM_TIM234_CCMR1_IC2PSC_8 3
+#define STM_TIM234_CCMR1_IC1F 4
+#define STM_TIM234_CCMR1_IC1F_NONE 0
+#define STM_TIM234_CCMR1_IC1F_CK_INT_N_2 1
+#define STM_TIM234_CCMR1_IC1F_CK_INT_N_4 2
+#define STM_TIM234_CCMR1_IC1F_CK_INT_N_8 3
+#define STM_TIM234_CCMR1_IC1F_DTS_2_N_6 4
+#define STM_TIM234_CCMR1_IC1F_DTS_2_N_8 5
+#define STM_TIM234_CCMR1_IC1F_DTS_4_N_6 6
+#define STM_TIM234_CCMR1_IC1F_DTS_4_N_8 7
+#define STM_TIM234_CCMR1_IC1F_DTS_8_N_6 8
+#define STM_TIM234_CCMR1_IC1F_DTS_8_N_8 9
+#define STM_TIM234_CCMR1_IC1F_DTS_16_N_5 10
+#define STM_TIM234_CCMR1_IC1F_DTS_16_N_6 11
+#define STM_TIM234_CCMR1_IC1F_DTS_16_N_8 12
+#define STM_TIM234_CCMR1_IC1F_DTS_32_N_5 13
+#define STM_TIM234_CCMR1_IC1F_DTS_32_N_6 14
+#define STM_TIM234_CCMR1_IC1F_DTS_32_N_8 15
+#define STM_TIM234_CCMR1_IC1PSC 2
+#define STM_TIM234_CCMR1_IC1PSC_NONE 0
+#define STM_TIM234_CCMR1_IC1PSC_2 1
+#define STM_TIM234_CCMR1_IC1PSC_4 2
+#define STM_TIM234_CCMR1_IC1PSC_8 3
+
+#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
+#define STM_TIM234_CCMR2_OC4M_SET_LOW_ON_MATCH 2
+#define STM_TIM234_CCMR2_OC4M_TOGGLE 3
+#define STM_TIM234_CCMR2_OC4M_FORCE_LOW 4
+#define STM_TIM234_CCMR2_OC4M_FORCE_HIGH 5
+#define STM_TIM234_CCMR2_OC4M_PWM_MODE_1 6
+#define STM_TIM234_CCMR2_OC4M_PWM_MODE_2 7
+#define STM_TIM234_CCMR2_OC4M_MASK 7UL
+#define STM_TIM234_CCMR2_OC4PE 11
+#define STM_TIM234_CCMR2_OC4FE 10
+#define STM_TIM234_CCMR2_CC4S 8
+#define STM_TIM234_CCMR2_CC4S_OUTPUT 0
+#define STM_TIM234_CCMR2_CC4S_INPUT_TI4 1
+#define STM_TIM234_CCMR2_CC4S_INPUT_TI3 2
+#define STM_TIM234_CCMR2_CC4S_INPUT_TRC 3
+#define STM_TIM234_CCMR2_CC4S_MASK 3UL
+
+#define STM_TIM234_CCMR2_OC3CE 7
+#define STM_TIM234_CCMR2_OC3M 4
+#define STM_TIM234_CCMR2_OC3M_FROZEN 0
+#define STM_TIM234_CCMR2_OC3M_SET_HIGH_ON_MATCH 1
+#define STM_TIM234_CCMR2_OC3M_SET_LOW_ON_MATCH 2
+#define STM_TIM234_CCMR2_OC3M_TOGGLE 3
+#define STM_TIM234_CCMR2_OC3M_FORCE_LOW 4
+#define STM_TIM234_CCMR2_OC3M_FORCE_HIGH 5
+#define STM_TIM234_CCMR2_OC3M_PWM_MODE_1 6
+#define STM_TIM234_CCMR2_OC3M_PWM_MODE_2 7
+#define STM_TIM234_CCMR2_OC3M_MASK 7UL
+#define STM_TIM234_CCMR2_OC3PE 3
+#define STM_TIM234_CCMR2_OC3FE 2
+#define STM_TIM234_CCMR2_CC3S 0
+#define STM_TIM234_CCMR2_CC3S_OUTPUT 0
+#define STM_TIM234_CCMR2_CC3S_INPUT_TI3 1
+#define STM_TIM234_CCMR2_CC3S_INPUT_TI4 2
+#define STM_TIM234_CCMR2_CC3S_INPUT_TRC 3
+#define STM_TIM234_CCMR2_CC3S_MASK 3UL
+
+#define STM_TIM234_CCER_CC4NP 15
+#define STM_TIM234_CCER_CC4P 13
+#define STM_TIM234_CCER_CC4P_ACTIVE_HIGH 0
+#define STM_TIM234_CCER_CC4P_ACTIVE_LOW 1
+#define STM_TIM234_CCER_CC4E 12
+#define STM_TIM234_CCER_CC3NP 11
+#define STM_TIM234_CCER_CC3P 9
+#define STM_TIM234_CCER_CC3P_ACTIVE_HIGH 0
+#define STM_TIM234_CCER_CC3P_ACTIVE_LOW 1
+#define STM_TIM234_CCER_CC3E 8
+#define STM_TIM234_CCER_CC2NP 7
+#define STM_TIM234_CCER_CC2P 5
+#define STM_TIM234_CCER_CC2P_ACTIVE_HIGH 0
+#define STM_TIM234_CCER_CC2P_ACTIVE_LOW 1
+#define STM_TIM234_CCER_CC2E 4
+#define STM_TIM234_CCER_CC1NP 3
+#define STM_TIM234_CCER_CC1P 1
+#define STM_TIM234_CCER_CC1P_ACTIVE_HIGH 0
+#define STM_TIM234_CCER_CC1P_ACTIVE_LOW 1
+#define STM_TIM234_CCER_CC1E 0
+
+struct stm_tim67 {
+ vuint32_t cr1;
+ vuint32_t cr2;
+ uint32_t _unused_08;
+ vuint32_t dier;
+
+ vuint32_t sr;
+ vuint32_t egr;
+ uint32_t _unused_18;
+ uint32_t _unused_1c;
+
+ uint32_t _unused_20;
+ vuint32_t cnt;
+ vuint32_t psc;
+ vuint32_t arr;
+};
+
+extern struct stm_tim67 stm_tim6;
+
+#define STM_TIM67_CR1_ARPE (7)
+#define STM_TIM67_CR1_OPM (3)
+#define STM_TIM67_CR1_URS (2)
+#define STM_TIM67_CR1_UDIS (1)
+#define STM_TIM67_CR1_CEN (0)
+
+#define STM_TIM67_CR2_MMS (4)
+#define STM_TIM67_CR2_MMS_RESET 0
+#define STM_TIM67_CR2_MMS_ENABLE 1
+#define STM_TIM67_CR2_MMS_UPDATE 2
+#define STM_TIM67_CR2_MMS_MASK 7UL
+
+#define STM_TIM67_DIER_UDE (8)
+#define STM_TIM67_DIER_UIE (0)
+
+#define STM_TIM67_SR_UIF (0)
+
+#define STM_TIM67_EGR_UG (0)
+
+#define isr_decl(name) void stm_ ## name ## _isr(void)
+
+isr_decl(halt);
+isr_decl(ignore);
+
+isr_decl(nmi);
+isr_decl(hardfault);
+isr_decl(memmanage);
+isr_decl(busfault);
+isr_decl(usagefault);
+isr_decl(svc);
+isr_decl(debugmon);
+isr_decl(pendsv);
+isr_decl(systick);
+isr_decl(wwdg);
+isr_decl(pvd);
+isr_decl(tamper_stamp);
+isr_decl(rtc_wkup);
+isr_decl(flash);
+isr_decl(rcc);
+isr_decl(exti0);
+isr_decl(exti1);
+isr_decl(exti2);
+isr_decl(exti3);
+isr_decl(exti4);
+isr_decl(dma1_channel1);
+isr_decl(dma1_channel2);
+isr_decl(dma1_channel3);
+isr_decl(dma1_channel4);
+isr_decl(dma1_channel5);
+isr_decl(dma1_channel6);
+isr_decl(dma1_channel7);
+isr_decl(adc1_2);
+isr_decl(usb_hp);
+isr_decl(usb_lp);
+isr_decl(can_rx1);
+isr_decl(can_sce);
+isr_decl(exti9_5);
+isr_decl(tim1_brk);
+isr_decl(tim1_up);
+isr_decl(tim1_trg_com);
+isr_decl(tim1_cc);
+isr_decl(tim2);
+isr_decl(tim3);
+isr_decl(tim4);
+isr_decl(i2c1_ev);
+isr_decl(i2c1_er);
+isr_decl(i2c2_ev);
+isr_decl(i2c2_er);
+isr_decl(spi1);
+isr_decl(spi2);
+isr_decl(usart1);
+isr_decl(usart2);
+isr_decl(usart3);
+isr_decl(exti15_10);
+isr_decl(rtc_alarm);
+isr_decl(usb_wakeup);
+isr_decl(tim8_brk);
+isr_decl(tim8_up);
+isr_decl(tim8_trg_com);
+isr_decl(tim8_cc);
+isr_decl(adc3);
+isr_decl(fsmc);
+isr_decl(sdio);
+isr_decl(tim5);
+isr_decl(spi3);
+isr_decl(uart4);
+isr_decl(uart5);
+isr_decl(tim6);
+isr_decl(tim7);
+isr_decl(dma2_channel1);
+isr_decl(dma2_channel2);
+isr_decl(dma2_channel3);
+isr_decl(dma2_channel4_5);
+
+#undef isr_decl
+
+#define STM_ISR_WWDG_POS 0
+#define STM_ISR_PVD_POS 1
+#define STM_ISR_TAMPER_STAMP_POS 2
+#define STM_ISR_RTC_WKUP_POS 3
+#define STM_ISR_FLASH_POS 4
+#define STM_ISR_RCC_POS 5
+#define STM_ISR_EXTI0_POS 6
+#define STM_ISR_EXTI1_POS 7
+#define STM_ISR_EXTI2_POS 8
+#define STM_ISR_EXTI3_POS 9
+#define STM_ISR_EXTI4_POS 10
+#define STM_ISR_DMA1_CHANNEL1_POS 11
+#define STM_ISR_DMA1_CHANNEL2_POS 12
+#define STM_ISR_DMA1_CHANNEL3_POS 13
+#define STM_ISR_DMA1_CHANNEL4_POS 14
+#define STM_ISR_DMA1_CHANNEL5_POS 15
+#define STM_ISR_DMA1_CHANNEL6_POS 16
+#define STM_ISR_DMA1_CHANNEL7_POS 17
+#define STM_ISR_ADC1_2_POS 18
+#define STM_ISR_USB_HP_POS 19
+#define STM_ISR_USB_LP_POS 20
+#define STM_ISR_CAN_RX1_POS 21
+#define STM_ISR_CAN_SCE_POS 22
+#define STM_ISR_EXTI9_5_POS 23
+#define STM_ISR_TIM1_BRK_POS 24
+#define STM_ISR_TIM1_UP_POS 25
+#define STM_ISR_TIM1_TRG_COM_POS 26
+#define STM_ISR_TIM1_CC_POS 27
+#define STM_ISR_TIM2_POS 28
+#define STM_ISR_TIM3_POS 29
+#define STM_ISR_TIM4_POS 30
+#define STM_ISR_I2C1_EV_POS 31
+#define STM_ISR_I2C1_ER_POS 32
+#define STM_ISR_I2C2_EV_POS 33
+#define STM_ISR_I2C2_ER_POS 34
+#define STM_ISR_SPI1_POS 35
+#define STM_ISR_SPI2_POS 36
+#define STM_ISR_USART1_POS 37
+#define STM_ISR_USART2_POS 38
+#define STM_ISR_USART3_POS 39
+#define STM_ISR_EXTI15_10_POS 40
+#define STM_ISR_RTC_ALARM_POS 41
+#define STM_ISR_USB_WAKEUP_POS 42
+#define STM_ISR_TIM8_BRK_POS 43
+#define STM_ISR_TIM8_UP_POS 44
+#define STM_ISR_TIM8_TRG_COM_POS 45
+#define STM_ISR_TIM8_CC_POS 46
+#define STM_ISR_ADC3_POS 47
+#define STM_ISR_FSMC_POS 48
+#define STM_ISR_SDIO_POS 49
+#define STM_ISR_TIM5_POS 50
+#define STM_ISR_SPI3_POS 51
+#define STM_ISR_UART4_POS 52
+#define STM_ISR_UART5_POS 53
+#define STM_ISR_TIM6_POS 54
+#define STM_ISR_TIM7_POS 55
+#define STM_ISR_DMA2_CHANNEL1_POS 56
+#define STM_ISR_DMA2_CHANNEL2_POS 57
+#define STM_ISR_DMA2_CHANNEL3_POS 58
+#define STM_ISR_DMA3_CHANNEL4_5_POS 59
+
+#endif
--- /dev/null
+ao_product.h
+nucleo-*.elf
--- /dev/null
+include ../stm32f1/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_pins.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_exti.h \
+ ao_product.h \
+ ao_st7565.h \
+ stm32f1.h \
+ Makefile
+
+ALTOS_SRC = \
+ ao_clock.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_boot_chain.c \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_led.c \
+ ao_task.c \
+ ao_panic.c \
+ ao_stdio.c \
+ ao_serial_stm.c \
+ ao_usb_stm.c \
+ ao_dma_stm.c \
+ ao_spi_stm.c \
+ ao_st7565.c \
+ ao_rect.c \
+ ao_text.c \
+ ao_box.c \
+ ao_copy.c \
+ ao_blt.c \
+ ao_line.c \
+ ao_logo.c \
+ ao_poly.c \
+ BitstreamVeraSans-Roman-58.c \
+ BitstreamVeraSans-Roman-24.c \
+ BitstreamVeraSans-Roman-10.c \
+ BitstreamVeraSans-Roman-12.c \
+ BenguiatGothicStd-Bold-26.c \
+ ao_cmd.c
+
+PRODUCT=Nucleo-f103
+PRODUCT_DEF=-DNUCLEO_F103
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(STM32F1_CFLAGS)
+
+PROGNAME=nucleo
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) hello.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ)
+ $(call quiet,CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
+ rm -f ao_product.h
+
+install:
+
+uninstall:
--- /dev/null
+/*
+ * Copyright © 2023 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.
+ */
+
+#define AO_HSE 1
+#define AO_HSE_BYPASS 1
+
+#define AO_SYSCLK 72000000
+#define AO_HCLK 72000000
+#define AO_APB1CLK 36000000
+#define AO_APB2CLK 72000000
+#define AO_ADCCLK 12000000
+
+#define AO_RCC_CFGR_USBPRE STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE STM_RCC_CFGR_PLLXTPRE_1
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE1_DIV_2
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+#define AO_RCC_CFGR_ADCPRE STM_RCC_CFGR_ADCPRE_6
+
+#define HAS_BEEP 0
+#define HAS_USB 1
+
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT (&stm_gpiob)
+#define AO_USB_PULLUP_PIN 12
+
+#define HAS_LED 1
+#define LED_0_PORT (&stm_gpioa)
+#define LED_0_PIN 5
+#define AO_LED_GREEN (1 << 0)
+#define AO_LED_PANIC AO_LED_GREEN
+
+#define HAS_SERIAL_1 0
+#define USE_SERIAL_1_STDIN 0
+#define SERIAL_1_PA9_PA10 1
+
+#define HAS_SERIAL_2 1
+#define USE_SERIAL_2_STDIN 1
+#define SERIAL_2_PA2_PA3 1
+#define SERIAL_2_SPEED AO_SERIAL_SPEED_115200
+
+#define HAS_SPI_1 1
+#define SPI_1_PA5_PA6_PA7 1
+#define SPI_1_MODE_OUTPUT STM_GPIO_CR_MODE_OUTPUT_10MHZ
+
+/* Chip Select. LCD pin 1. nucleo PA4 = A2 */
+#define AO_ST7565_CS_PORT (&stm_gpioa) /* pin 1 */
+#define AO_ST7565_CS_PIN 4
+
+/* Reset. LCD pin 2. nucleo PA0 = A0 */
+#define AO_ST7565_RESET_PORT (&stm_gpioa) /* pin 2 */
+#define AO_ST7565_RESET_PIN 0
+
+/* A0. LCD pin 3. nucleo PA1 = A1 */
+#define AO_ST7565_A0_PORT (&stm_gpioa) /* pin 3 */
+#define AO_ST7565_A0_PIN 1
+
+/* SCLK. LCD DB6 pin 12. nucleo PA5 = D13 */
+/* MOSI. LCD DB7 pin 13. nucleo PA7 = D11 */
+#define AO_ST7565_SPI_BUS (AO_SPI_1_PA5_PA6_PA7 | AO_SPI_MODE_3)
+#define AO_ST7565_WIDTH 128
+#define AO_ST7565_HEIGHT 64
+#define AO_ST7565_BIAS ST7565_LCD_BIAS_1_9
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=nucleo-f103
+include $(TOPDIR)/stm32f1/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; 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_
+
+#define AO_HSE 1
+#define AO_HSE_BYPASS 1
+
+#define AO_SYSCLK 72000000
+#define AO_HCLK 72000000
+#define AO_APB1CLK 36000000
+#define AO_APB2CLK 72000000
+#define AO_ADCCLK 12000000
+
+#define AO_RCC_CFGR_USBPRE STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE STM_RCC_CFGR_PLLXTPRE_1
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE1_DIV_2
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+#define AO_RCC_CFGR_ADCPRE STM_RCC_CFGR_ADCPRE_6
+
+#include <ao_flash_stm_pins.h>
+
+/* USB DM is on PA11 (CN10 pin 14). USB DP is on PA12 (CN10 pin 12) */
+
+/* For pullup, we'll use PB6 (CN10 pin 16) */
+
+#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
+
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT (&stm_gpiob)
+#define AO_USB_PULLUP_PIN 12
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+/*
+ * Copyright © 2023 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_st7565.h>
+
+#define WIDTH AO_ST7565_WIDTH
+#define HEIGHT AO_ST7565_HEIGHT
+#define STRIDE AO_BITMAP_STRIDE(WIDTH)
+
+static uint32_t image[STRIDE * HEIGHT];
+
+static struct ao_bitmap fb = {
+ .base = image,
+ .stride = STRIDE,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .damage = AO_BOX_INIT,
+};
+
+static void
+ao_st7565_test(void)
+{
+ ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+ ao_st7565_update(&fb);
+ ao_text(&fb, &BitstreamVeraSans_Roman_24_font,
+ 0, 20, "hello world", AO_BLACK, AO_COPY);
+ ao_st7565_update(&fb);
+}
+
+static int16_t x1 = 32, _y1 = 10, x2 = 32, y2 = 40;
+static int16_t dx1 = 2, dy1 = 2, dx2 = -2, dy2 = -1;
+
+#define bounds(v,m,M,d) \
+ if (v < m) { \
+ v = m + m - v; \
+ d = -d; \
+ } else if (v > M) { \
+ v = M - (v - M); \
+ d = -d; \
+ }
+
+static void
+ao_st7565_line(void)
+{
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+ ao_line(&fb, x1, _y1, x2, y2, AO_BLACK, AO_COPY);
+ ao_st7565_update(&fb);
+ x1 += dx1;
+ _y1 += dy1;
+ x2 += dx2;
+ y2 += dy2;
+ printf("%d,%d - %d,%d\n", x1, _y1, x2, y2);
+ fflush(stdout);
+ bounds(x1, 0, WIDTH, dx1);
+ bounds(x2, 0, WIDTH, dx2);
+ bounds(_y1, 0, HEIGHT, dy1);
+ bounds(y2, 0, HEIGHT, dy2);
+ ao_delay(AO_MS_TO_TICKS(200));
+ }
+}
+
+static const float pad_volts = 12.3f;
+static const float lco_volts = 4.1f;
+static const int rssi = -30;
+
+static int boxes[] = { 1, 2, 3, 5, 8, 11, 13, 17, 19, 23, 29, 31, 37, 62, 97 };
+
+//static int max_box = 97;
+
+#define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+static bool
+valid_box(int box)
+{
+ size_t i;
+ if (box == 0)
+ return true;
+ for (i = 0; i < ARRAYSIZE(boxes); i++)
+ if (boxes[i] == box)
+ return true;
+ return false;
+}
+
+#if 0
+static void
+next_box(void)
+{
+ for (int n = box_number + 1; n <= max_box; n++)
+ if (valid_box(n)) {
+ box_number = n;
+ return;
+ }
+ box_number = 0;
+}
+
+static void
+prev_box(void)
+{
+ for (int n = box_number - 1; n >= 0; n--)
+ if (valid_box(n)) {
+ box_number = n;
+ return;
+ }
+ box_number = max_box;
+}
+#endif
+
+static const struct ao_transform logo_transform = {
+ .x_scale = 48, .x_off = 2,
+ .y_scale = 48, .y_off = 0,
+};
+
+#define BIG_FONT BitstreamVeraSans_Roman_58_font
+#define VOLT_FONT BitstreamVeraSans_Roman_58_font
+#define SMALL_FONT BitstreamVeraSans_Roman_12_font
+#define TINY_FONT BitstreamVeraSans_Roman_10_font
+#define LOGO_FONT BenguiatGothicStd_Bold_26_font
+
+#define LABEL_Y (int16_t) (SMALL_FONT.ascent)
+#define VALUE_Y (int16_t) (LABEL_Y + BIG_FONT.ascent + 5)
+#define BOX_X 2
+#define PAD_X 90
+#define BOX_LABEL_X 30
+#define VOLT_LABEL_X 25
+#define RSSI_LABEL_X 15
+#define PAD_LABEL_X 95
+#define SEP_X (PAD_X - 8)
+#define SCAN_X (WIDTH - 100) / 2
+#define SCAN_Y 50
+#define SCAN_HEIGHT 3
+#define FOUND_Y 63
+#define FOUND_X 6
+#define FOUND_WIDTH 17
+#define MAX_VALID (WIDTH / FOUND_WIDTH)
+
+static int16_t box_number = 88;
+static int16_t pad_number = 8;
+
+static void
+ao_st7565_poly(void)
+{
+ int16_t scan_number;
+ char str[8];
+ int i;
+ int v;
+ int last_box;
+ int16_t b;
+
+ for (scan_number = 0; scan_number < 100; scan_number++) {
+ ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+ ao_logo(&fb, &logo_transform, &LOGO_FONT, AO_BLACK, AO_COPY);
+ if (scan_number) {
+ ao_rect(&fb, SCAN_X, SCAN_Y, (int16_t) scan_number, SCAN_HEIGHT, AO_BLACK, AO_COPY);
+ b = 0;
+ v = 0;
+ last_box = 0;
+ for (i = scan_number; i > 1; i--) {
+ if (valid_box(i)) {
+ if (!last_box)
+ last_box = i;
+ v++;
+ if (v == MAX_VALID)
+ break;
+ }
+ }
+ for (; i <= scan_number; i++) {
+ if (valid_box(i)) {
+ sprintf(str, "%02d%s", i, i == last_box ? "" : ",");
+ ao_text(&fb, &TINY_FONT, (int16_t) (FOUND_X + FOUND_WIDTH * b),
+ FOUND_Y, str, AO_BLACK, AO_COPY);
+ b++;
+ }
+ }
+ }
+ ao_st7565_update(&fb);
+ ao_delay(AO_MS_TO_TICKS(50));
+ }
+ ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+ switch (box_number) {
+ case 0:
+ sprintf(str, "%4.1f", lco_volts);
+ ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+ ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, "LCO Battery", AO_BLACK, AO_COPY);
+ break;
+ default:
+ switch (pad_number) {
+ case -1:
+ sprintf(str, "%4.1f", pad_volts);
+ ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+ ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, "Pad Battery", AO_BLACK, AO_COPY);
+ break;
+ case 0:
+ sprintf(str, "%4d", rssi);
+ ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+ ao_text(&fb, &SMALL_FONT, RSSI_LABEL_X, LABEL_Y, "Signal Strength", AO_BLACK, AO_COPY);
+ break;
+ default:
+ sprintf(str, "%02d", box_number);
+ ao_text(&fb, &BIG_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+ ao_text(&fb, &SMALL_FONT, BOX_LABEL_X, LABEL_Y, "Box", AO_BLACK, AO_COPY);
+
+ sprintf(str, "%d", pad_number);
+ ao_text(&fb, &BIG_FONT, PAD_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+ ao_text(&fb, &SMALL_FONT, PAD_LABEL_X, LABEL_Y, "Pad", AO_BLACK, AO_COPY);
+
+ ao_rect(&fb, SEP_X, 0, 2, HEIGHT, AO_BLACK, AO_COPY);
+ }
+ break;
+ }
+ ao_st7565_update(&fb);
+}
+
+const struct ao_cmds ao_st7565_cmds[] = {
+ { ao_st7565_test, "g\0Test ST7565 display" },
+ { ao_st7565_line, "l\0Draw lines" },
+ { ao_st7565_poly, "p\0Draw polygon" },
+ { 0, NULL },
+};
+
+int main(void)
+{
+ ao_clock_init();
+ ao_led_init();
+ ao_timer_init();
+ ao_task_init();
+ ao_dma_init();
+ ao_spi_init();
+ ao_serial_init();
+ ao_usb_init();
+ ao_st7565_init();
+ ao_cmd_init();
+ ao_cmd_register(ao_st7565_cmds);
+ ao_start_scheduler();
+}
--- /dev/null
+__flash = 0x20000000;
+__flash_size = 4k;
+__ram = 0x20001000;
+__ram_size = 4k;
+__stack_size = 512;
+
+INCLUDE picolibc.ld
/* Companion bus wants something no faster than 200kHz */
static inline uint32_t
-ao_spi_speed(uint32_t hz)
+ao_spi_speed(int index, uint32_t hz)
{
+ (void) index;
if (hz >= 4000000) return _AO_SPI_SPEED_4MHz;
if (hz >= 2000000) return _AO_SPI_SPEED_2MHz;
if (hz >= 1000000) return _AO_SPI_SPEED_1MHz;
#define _AO_SPI_SPEED_187500Hz STM_SPI_CR1_BR_PCLK_256
static inline uint32_t
-ao_spi_speed(uint32_t hz)
+ao_spi_speed(int index, uint32_t hz)
{
+ (void) index;
if (hz >=24000000) return _AO_SPI_SPEED_24MHz;
if (hz >=12000000) return _AO_SPI_SPEED_12MHz;
if (hz >= 6000000) return _AO_SPI_SPEED_6MHz;
*/
#define AO_ADC_REFERENCE_DV 33
+#define AO_LCO_DRAG_RACE_BOX 1
+
#endif /* _AO_PINS_H_ */
#define AO_BUTTON_FIRE 1
+#define AO_LCO_DRAG_RACE_BOX 1
+
#endif /* _AO_PINS_H_ */
#define AO_BUTTON_FIRE 1
+#define AO_LCO_DRAG_RACE_BOX 1
+
#endif /* _AO_PINS_H_ */
static uint8_t ao_lco_display_mutex;
void
-ao_lco_show_pad(uint8_t pad)
+ao_lco_show_pad(int8_t pad)
{
ao_mutex_get(&ao_lco_display_mutex);
ao_seven_segment_set(AO_LCO_PAD_DIGIT, (uint8_t) (pad | (ao_lco_drag_race << 4)));
(0 << 6))
void
-ao_lco_show_box(uint16_t box)
+ao_lco_show_box(int16_t box)
{
ao_mutex_get(&ao_lco_display_mutex);
ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, (uint8_t) (box % 10 | (ao_lco_drag_race << 4)));
}
static void
-ao_lco_show_voltage(uint16_t decivolts)
+ao_lco_show_value(uint16_t value, uint8_t point)
{
- uint8_t tens, ones, tenths;
-
- PRINTD("voltage %d\n", decivolts);
- tenths = (uint8_t) (decivolts % 10);
- ones = (uint8_t) ((decivolts / 10) % 10);
- tens = (uint8_t) ((decivolts / 100) % 10);
+ uint8_t hundreds, tens, ones;
+
+ PRINTD("value %d\n", value);
+ ones = (uint8_t) (value % 10);
+ tens = (uint8_t) ((value / 10) % 10);
+ hundreds = (uint8_t) ((value / 100) % 10);
+ switch (point) {
+ case 2:
+ hundreds |= 0x10;
+ break;
+ case 1:
+ tens |= 0x10;
+ break;
+ case 0:
+ ones |= 0x10;
+ break;
+ default:
+ break;
+ }
ao_mutex_get(&ao_lco_display_mutex);
- ao_seven_segment_set(AO_LCO_PAD_DIGIT, tenths);
- ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, ones | 0x10);
- ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, tens);
+ ao_seven_segment_set(AO_LCO_PAD_DIGIT, ones);
+ ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, tens);
+ ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, hundreds);
ao_mutex_put(&ao_lco_display_mutex);
}
-void
-ao_lco_show(void)
+static void
+ao_lco_show_lco_voltage(void)
{
- if (ao_lco_pad == AO_LCO_PAD_VOLTAGE) {
- ao_lco_show_voltage(ao_pad_query.battery);
- } else {
- ao_lco_show_pad(ao_lco_pad);
- ao_lco_show_box(ao_lco_box);
- }
+ struct ao_adc packet;
+ int16_t decivolt;
+
+ ao_adc_single_get(&packet);
+ decivolt = ao_battery_decivolt(packet.v_batt);
+ ao_lco_show_value((uint16_t) decivolt, 1);
}
-uint8_t
-ao_lco_box_present(uint16_t box)
+void
+ao_lco_show(void)
{
- if (box >= AO_PAD_MAX_BOXES)
- return 0;
- return (ao_lco_box_mask[AO_LCO_MASK_ID(box)] >> AO_LCO_MASK_SHIFT(box)) & 1;
+ switch (ao_lco_box) {
+ case AO_LCO_LCO_VOLTAGE:
+ ao_lco_show_lco_voltage();
+ break;
+ default:
+ switch (ao_lco_pad) {
+ case AO_LCO_PAD_VOLTAGE:
+ ao_lco_show_value(ao_pad_query.battery, 1);
+ break;
+ case AO_LCO_PAD_RSSI:
+ if (!(ao_lco_valid[ao_lco_box] & AO_LCO_VALID_LAST))
+ ao_lco_show_value(888, 0);
+ else
+ ao_lco_show_value((uint16_t) (-ao_radio_cmac_rssi), 0);
+ break;
+ default:
+ ao_lco_show_pad(ao_lco_pad);
+ ao_lco_show_box(ao_lco_box);
+ break;
+ }
+ }
}
static void
}
}
-static void
-ao_lco_step_box(int8_t dir)
-{
- int32_t new_box = (int32_t) ao_lco_box;
-
- do {
- new_box += dir;
- if (new_box > ao_lco_max_box)
- new_box = ao_lco_min_box;
- else if (new_box < ao_lco_min_box)
- new_box = ao_lco_max_box;
- if (new_box == ao_lco_box)
- break;
- } while (!ao_lco_box_present((uint16_t) new_box));
- ao_lco_set_box((uint16_t) new_box);
-}
-
static struct ao_task ao_lco_drag_task;
static void
static void
ao_lco_batt_voltage(void)
{
- struct ao_adc packet;
- int16_t decivolt;
-
- ao_adc_single_get(&packet);
- decivolt = ao_battery_decivolt(packet.v_batt);
- ao_lco_show_voltage((uint16_t) decivolt);
+ ao_lco_show_lco_voltage();
ao_delay(AO_MS_TO_TICKS(1000));
}
--- /dev/null
+ao_product.h
+telelco*.elf
--- /dev/null
+#
+# AltOS build for TeleLCO v3.0
+#
+#
+
+STM32F1_LINKER_SCRIPT=altos-128.ld
+
+include ../stm32f1/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_boot.h \
+ ao_companion.h \
+ ao_data.h \
+ ao_sample.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_lco.h \
+ ao_lco_cmd.h \
+ ao_lco_func.h \
+ ao_radio_spi.h \
+ ao_radio_cmac.h \
+ ao_cc1200_CC1200.h \
+ ao_cc1200.h \
+ ao_st7565.h \
+ ao_font.h \
+ ao_logo.h \
+ stm32f1.h
+
+#
+# Common AltOS sources
+#
+
+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_clock.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_freq.c \
+ ao_adc_single_stm.c \
+ ao_dma_stm.c \
+ ao_spi_stm.c \
+ ao_beep_stm.c \
+ ao_convert_volt.c \
+ ao_fast_timer.c \
+ ao_pwm_stm.c \
+ ao_eeprom.c \
+ ao_flash_stm.c \
+ ao_usb_stm.c \
+ ao_exti_stm.c \
+ ao_cc1200.c \
+ ao_radio_cmac.c \
+ ao_aes.c \
+ ao_aes_tables.c \
+ ao_st7565.c \
+ ao_rect.c \
+ ao_text.c \
+ ao_text_width.c \
+ ao_box.c \
+ ao_copy.c \
+ ao_blt.c \
+ ao_line.c \
+ ao_logo.c \
+ ao_poly.c \
+ BitstreamVeraSans-Roman-58.c \
+ BitstreamVeraSans-Roman-24.c \
+ BitstreamVeraSans-Roman-10.c \
+ BitstreamVeraSans-Roman-12.c \
+ BenguiatGothicStd-Bold-26.c \
+ ao_quadrature.c \
+ ao_button.c \
+ ao_event.c \
+ ao_lco_bits.c \
+ ao_lco_v3.c \
+ ao_lco_cmd.c \
+ ao_lco_func.c \
+ ao_radio_cmac_cmd.c
+
+PRODUCT=TeleLCO-v3.0
+PRODUCT_DEF=-DTELELCO
+IDPRODUCT=0x0023
+
+CFLAGS = $(PRODUCT_DEF) $(STM32F1_CFLAGS)
+
+PROGNAME=telelco-v3.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_telelco.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos-128.ld
+ $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
+ rm -f ao_product.h
+
+ao_font.h:
+ cd ../draw && make ao_font.h ao_logo.h
+
+ao_logo.h: ao_font.h
+
+BitstreamVeraSans-Roman-58.c: ao_font.h
+BitstreamVeraSans-Roman-24.c: ao_font.h
+BitstreamVeraSans-Roman-10.c: ao_font.h
+BitstreamVeraSans-Roman-12.c: ao_font.h
+BenguiatGothicStd-Bold-26.c: ao_font.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; 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_lco.h>
+#include <ao_event.h>
+#include <ao_quadrature.h>
+#include <ao_radio_cmac.h>
+#include <ao_st7565.h>
+#include <ao_adc_single.h>
+#include <ao_pwm.h>
+
+#define WIDTH AO_ST7565_WIDTH
+#define HEIGHT AO_ST7565_HEIGHT
+#define STRIDE AO_BITMAP_STRIDE(WIDTH)
+
+static uint32_t image[STRIDE * HEIGHT];
+
+static struct ao_bitmap fb = {
+ .base = image,
+ .stride = STRIDE,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .damage = AO_BOX_INIT,
+};
+
+static const struct ao_transform logo_transform = {
+ .x_scale = 48, .x_off = 2,
+ .y_scale = 48, .y_off = 0,
+};
+
+static const struct ao_transform show_transform = {
+ .x_scale = 36, .x_off = 100,
+ .y_scale = 36, .y_off = 0,
+};
+
+#define BIG_FONT BitstreamVeraSans_Roman_58_font
+#define VOLT_FONT BitstreamVeraSans_Roman_58_font
+#define SMALL_FONT BitstreamVeraSans_Roman_12_font
+#define TINY_FONT BitstreamVeraSans_Roman_10_font
+#define LOGO_FONT BenguiatGothicStd_Bold_26_font
+
+#define LABEL_Y (int16_t) (SMALL_FONT.ascent)
+#define VALUE_Y (int16_t) (LABEL_Y + 5 + BIG_FONT.ascent)
+
+#define SEP_X 82
+#define SEP_WIDTH 2
+
+#define BOX_X (SEP_X / 2)
+#define PAD_X ((WIDTH + SEP_X + SEP_WIDTH) / 2)
+
+#define VALUE_LABEL_X 64
+#define RSSI_LABEL_X 15
+
+#define SCAN_X (WIDTH - 100) / 2
+#define SCAN_Y 50
+#define SCAN_HEIGHT 3
+#define FOUND_Y 63
+#define FOUND_X 3
+#define FOUND_WIDTH (WIDTH - 6)
+#define CONTRAST_LABEL_X 37
+#define CONTRAST_WIDTH 100
+#define CONTRAST_X (WIDTH - CONTRAST_WIDTH) / 2
+#define CONTRAST_Y 20
+#define CONTRAST_HEIGHT 20
+#define CONTRAST_VALUE_X 64
+#define CONTRAST_VALUE_Y (CONTRAST_Y + CONTRAST_HEIGHT + SMALL_FONT.ascent + 3)
+#define BACKLIGHT_LABEL_X 37
+#define BACKLIGHT_WIDTH 100
+#define BACKLIGHT_X (WIDTH - BACKLIGHT_WIDTH) / 2
+#define BACKLIGHT_Y 20
+#define BACKLIGHT_HEIGHT 20
+#define BACKLIGHT_VALUE_X 64
+#define BACKLIGHT_VALUE_Y (BACKLIGHT_Y + BACKLIGHT_HEIGHT + SMALL_FONT.ascent + 3)
+#define INFO_START_Y ((int16_t) (SMALL_FONT.ascent + 2))
+#define INFO_STEP_Y ((int16_t) (SMALL_FONT.ascent + 3))
+
+#define AO_LCO_DRAG_RACE_START_TIME AO_SEC_TO_TICKS(5)
+#define AO_LCO_DRAG_RACE_STOP_TIME AO_SEC_TO_TICKS(2)
+
+/* 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_BOX 0
+#define AO_LCO_SELECT_PAD 1
+
+static uint8_t ao_lco_display_mutex;
+
+static void
+_ao_center_text(int16_t x, int16_t y, const struct ao_font *font, const char *str)
+{
+ int16_t width = ao_text_width(font, str);
+ ao_text(&fb, font, x - width/2, y, str, AO_BLACK, AO_COPY);
+}
+
+static void
+_ao_lco_show_pad(int8_t pad)
+{
+ char str[5];
+
+ _ao_center_text(PAD_X, LABEL_Y, &SMALL_FONT, "Pad");
+ snprintf(str, sizeof(str), "%d", pad);
+ _ao_center_text(PAD_X, VALUE_Y, &BIG_FONT, str);
+}
+
+static void
+_ao_lco_show_box(int16_t box)
+{
+ char str[7];
+
+ _ao_center_text(BOX_X, LABEL_Y, &SMALL_FONT, "Bank");
+ snprintf(str, sizeof(str), "%d", box);
+ _ao_center_text(BOX_X, VALUE_Y, &BIG_FONT, str);
+}
+
+static void
+_ao_lco_show_voltage(uint16_t decivolts, const char *label)
+{
+ char str[7];
+
+ PRINTD("voltage %d\n", decivolts);
+ _ao_center_text(WIDTH/2, LABEL_Y, &SMALL_FONT, label);
+ snprintf(str, sizeof(str), "%d.%d", decivolts / 10, decivolts % 10);
+ _ao_center_text(WIDTH/2, VALUE_Y, &BIG_FONT, str);
+}
+
+static void
+_ao_lco_batt_voltage(void)
+{
+ struct ao_adc packet;
+ int16_t decivolt;
+
+ ao_adc_single_get(&packet);
+ decivolt = ao_battery_decivolt(packet.v_batt);
+ _ao_lco_show_voltage((uint16_t) decivolt, "LCO Battery");
+ ao_st7565_update(&fb);
+}
+
+static void
+_ao_lco_show_contrast(void)
+{
+ char buf[8];
+ uint8_t brightness = ao_st7565_get_brightness();
+ int16_t contrast = (int16_t) (brightness * CONTRAST_WIDTH / AO_LCO_MAX_CONTRAST);
+
+ _ao_center_text(WIDTH/2, LABEL_Y, &SMALL_FONT, "Contrast");
+ ao_rect(&fb, CONTRAST_X, CONTRAST_Y, contrast, CONTRAST_HEIGHT, AO_BLACK, AO_COPY);
+ snprintf(buf, sizeof(buf), "%d %%", brightness * 100 / AO_LCO_MAX_CONTRAST);
+ _ao_center_text(WIDTH/2, CONTRAST_VALUE_Y, &SMALL_FONT, buf);
+}
+
+static void
+_ao_lco_show_backlight(void)
+{
+ char buf[8];
+ int32_t backlight = ao_lco_get_backlight();
+ int16_t value = (int16_t) (backlight * BACKLIGHT_WIDTH / AO_LCO_MAX_BACKLIGHT);
+
+ _ao_center_text(WIDTH/2, LABEL_Y, &SMALL_FONT, "Backlight");
+ ao_rect(&fb, BACKLIGHT_X, BACKLIGHT_Y, value, BACKLIGHT_HEIGHT, AO_BLACK, AO_COPY);
+ snprintf(buf, sizeof(buf), "%ld %%", backlight * 100 / AO_LCO_MAX_BACKLIGHT);
+ _ao_center_text(WIDTH/2, BACKLIGHT_VALUE_Y, &SMALL_FONT, buf);
+}
+
+static int16_t info_y;
+
+static void
+_ao_lco_info(const char *format, ...)
+{
+ va_list a;
+ char buf[20];
+ va_start(a, format);
+ vsnprintf(buf, sizeof(buf), format, a);
+ va_end(a);
+ ao_text(&fb, &SMALL_FONT, 0, info_y, buf, AO_BLACK, AO_COPY);
+ info_y += INFO_STEP_Y;
+}
+
+static void
+_ao_lco_show_info(void)
+{
+ info_y = INFO_START_Y;
+ ao_logo_poly(&fb, &show_transform, AO_BLACK, AO_COPY);
+ _ao_lco_info("%s", ao_product);
+ _ao_lco_info("Version: %s", ao_version);
+ _ao_lco_info("Serial: %d", ao_serial_number);
+ _ao_lco_info("Callsign: %s", ao_config.callsign);
+ _ao_lco_info("Frequency: %ld.%03d",
+ ao_config.frequency / 1000,
+ (int) (ao_config.frequency % 1000));
+}
+
+static void
+_ao_lco_show_rssi(void)
+{
+ char label[20];
+ int16_t width;
+ snprintf(label, sizeof(label), "Bank %d RSSI", ao_lco_box);
+ width = ao_text_width(&SMALL_FONT, label);
+ ao_text(&fb, &SMALL_FONT, VALUE_LABEL_X - width / 2, LABEL_Y, label, AO_BLACK, AO_COPY);
+ if (!(ao_lco_valid[ao_lco_box] & AO_LCO_VALID_LAST))
+ strcpy(label, "---");
+ else
+ snprintf(label, sizeof(label), "%d", ao_radio_cmac_rssi);
+ width = ao_text_width(&VOLT_FONT, label);
+ ao_text(&fb, &VOLT_FONT, VALUE_LABEL_X - width / 2, VALUE_Y, label, AO_BLACK, AO_COPY);
+}
+
+static void
+_ao_lco_show_pad_battery(void)
+{
+ char label[20];
+ snprintf(label, sizeof(label), "Bank %d Battery", ao_lco_box);
+ _ao_lco_show_voltage(ao_pad_query.battery, label);
+}
+
+void
+ao_lco_show(void)
+{
+ ao_mutex_get(&ao_lco_display_mutex);
+ ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+ switch (ao_lco_box) {
+ case AO_LCO_LCO_VOLTAGE:
+ _ao_lco_batt_voltage();
+ break;
+ case AO_LCO_CONTRAST:
+ _ao_lco_show_contrast();
+ break;
+ case AO_LCO_BACKLIGHT:
+ _ao_lco_show_backlight();
+ break;
+ case AO_LCO_INFO:
+ _ao_lco_show_info();
+ break;
+ default:
+ switch (ao_lco_pad) {
+ case AO_LCO_PAD_RSSI:
+ _ao_lco_show_rssi();
+ break;
+ case AO_LCO_PAD_VOLTAGE:
+ _ao_lco_show_pad_battery();
+ break;
+ default:
+ _ao_lco_show_pad(ao_lco_pad);
+ _ao_lco_show_box(ao_lco_box);
+ ao_rect(&fb, SEP_X, 0, SEP_WIDTH, HEIGHT, AO_BLACK, AO_COPY);
+ }
+ break;
+ }
+ ao_st7565_update(&fb);
+ ao_mutex_put(&ao_lco_display_mutex);
+}
+
+static void
+ao_lco_set_select(void)
+{
+ if (ao_lco_armed) {
+ ao_led_off(AO_LED_PAD);
+ ao_led_off(AO_LED_BOX);
+ } else {
+ switch (ao_lco_select_mode) {
+ case AO_LCO_SELECT_PAD:
+ ao_led_off(AO_LED_BOX);
+ ao_led_on(AO_LED_PAD);
+ break;
+ case AO_LCO_SELECT_BOX:
+ ao_led_off(AO_LED_PAD);
+ ao_led_on(AO_LED_BOX);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+
+void
+ao_lco_set_contrast(int32_t contrast)
+{
+ ao_st7565_set_brightness((uint8_t) contrast);
+}
+
+int32_t
+ao_lco_get_contrast(void)
+{
+ return (int32_t) ao_st7565_get_brightness();
+}
+
+static uint16_t ao_backlight;
+
+void
+ao_lco_set_backlight(int32_t backlight)
+{
+ ao_backlight = (uint16_t) backlight;
+ ao_pwm_set(AO_LCD_BL_PWM_CHAN, ao_backlight);
+}
+
+int32_t
+ao_lco_get_backlight(void)
+{
+ return (int32_t) ao_backlight;
+}
+
+static struct ao_task ao_lco_drag_task;
+
+static void
+ao_lco_drag_monitor(void)
+{
+ AO_TICK_TYPE delay = ~0UL;
+ AO_TICK_TYPE now;
+
+ ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+ for (;;) {
+ PRINTD("Drag monitor count %d delay %lu\n", ao_lco_drag_beep_count, (unsigned long) delay);
+ if (delay == (AO_TICK_TYPE) ~0)
+ ao_sleep(&ao_lco_drag_beep_count);
+ else
+ ao_sleep_for(&ao_lco_drag_beep_count, delay);
+
+ delay = ~0UL;
+ now = ao_time();
+ delay = ao_lco_drag_warn_check(now, delay);
+ delay = ao_lco_drag_beep_check(now, delay);
+ }
+}
+
+static void
+ao_lco_input(void)
+{
+ static struct ao_event event;
+
+ for (;;) {
+ ao_event_get(&event);
+ PRINTE("event type %d unit %d value %ld\n",
+ event.type, event.unit, (long) event.value);
+ switch (event.type) {
+ case AO_EVENT_QUADRATURE:
+ switch (event.unit) {
+ case AO_QUADRATURE_SELECT:
+ if (!ao_lco_armed) {
+ switch (ao_lco_select_mode) {
+ case AO_LCO_SELECT_PAD:
+ ao_lco_step_pad((int8_t) event.value);
+ break;
+ case AO_LCO_SELECT_BOX:
+ ao_lco_step_box((int8_t) event.value);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ case AO_EVENT_BUTTON:
+ switch (event.unit) {
+ case AO_BUTTON_ARM:
+ ao_lco_set_armed((uint8_t) event.value);
+ ao_lco_set_select();
+ break;
+ case AO_BUTTON_FIRE:
+ if (ao_lco_armed)
+ ao_lco_set_firing((uint8_t) event.value);
+ break;
+ case AO_BUTTON_DRAG_SELECT:
+ if (event.value)
+ ao_lco_toggle_drag();
+ break;
+ case AO_BUTTON_DRAG_MODE:
+ if (event.value)
+ ao_lco_drag_enable();
+ else
+ ao_lco_drag_disable();
+ break;
+ case AO_BUTTON_ENCODER_SELECT:
+ if (event.value) {
+ if (!ao_lco_armed) {
+ ao_lco_select_mode = 1 - ao_lco_select_mode;
+ ao_lco_set_select();
+ }
+ }
+ break;
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * Light up everything for a second at power on to let the user
+ * visually inspect the system for correct operation
+ */
+static void
+ao_lco_display_test(void)
+{
+ ao_led_on(AO_LEDS_AVAILABLE);
+ ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_BLACK, AO_COPY);
+ ao_st7565_update(&fb);
+ ao_delay(AO_MS_TO_TICKS(250));
+ ao_led_off(AO_LEDS_AVAILABLE);
+}
+
+static struct ao_task ao_lco_input_task;
+static struct ao_task ao_lco_monitor_task;
+static struct ao_task ao_lco_arm_warn_task;
+static struct ao_task ao_lco_igniter_status_task;
+
+static int16_t found_width;
+#define MAX_FOUND 32
+static int16_t found_boxes[MAX_FOUND];
+static uint8_t nfound;
+
+void
+ao_lco_search_start(void)
+{
+ ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+ ao_logo(&fb, &logo_transform, &LOGO_FONT, AO_BLACK, AO_COPY);
+ found_width = 0;
+ nfound = 0;
+}
+
+void
+ao_lco_search_box_check(int16_t box)
+{
+ if (box > 0)
+ ao_rect(&fb, SCAN_X, SCAN_Y, box, SCAN_HEIGHT, AO_BLACK, AO_COPY);
+ ao_st7565_update(&fb);
+}
+
+void
+ao_lco_search_box_present(int16_t box)
+{
+ char str[8];
+ int16_t width;
+ int16_t box_top = FOUND_Y - TINY_FONT.ascent;
+ int16_t x;
+ uint8_t n;
+
+ snprintf(str, sizeof(str), "%s%u", nfound ? ", " : "", box);
+ width = ao_text_width(&TINY_FONT, str);
+ while (found_width + width > FOUND_WIDTH || nfound == MAX_FOUND)
+ {
+ snprintf(str, sizeof(str), "%u, ", found_boxes[0]);
+ found_width -= ao_text_width(&TINY_FONT, str);
+ memmove(&found_boxes[0], &found_boxes[1], (nfound - 1) * sizeof (int16_t));
+ nfound--;
+ }
+ found_boxes[nfound++] = box;
+
+ ao_rect(&fb, FOUND_X, FOUND_Y - TINY_FONT.ascent, FOUND_WIDTH, HEIGHT - box_top, AO_WHITE, AO_COPY);
+ x = FOUND_X;
+ for (n = 0; n < nfound; n++) {
+ snprintf(str, sizeof(str), "%s%u", n ? ", " : "", found_boxes[n]);
+ int16_t next_x = ao_text(&fb, &TINY_FONT, x, FOUND_Y, str, AO_BLACK, AO_COPY);
+ x = next_x;
+ }
+ found_width = x - FOUND_X;
+}
+
+void
+ao_lco_search_done(void)
+{
+ ao_st7565_update(&fb);
+}
+
+static void
+ao_lco_main(void)
+{
+ ao_lco_display_test();
+ ao_lco_search();
+ ao_add_task(&ao_lco_input_task, ao_lco_input, "lco input");
+ ao_add_task(&ao_lco_arm_warn_task, ao_lco_arm_warn, "lco arm warn");
+ ao_add_task(&ao_lco_igniter_status_task, ao_lco_igniter_status, "lco igniter status");
+ ao_add_task(&ao_lco_drag_task, ao_lco_drag_monitor, "drag race");
+ ao_lco_monitor();
+}
+
+#if DEBUG
+static void
+ao_lco_set_debug(void)
+{
+ uint32_t r = ao_cmd_decimal();
+ 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
+
+void
+ao_lco_init(void)
+{
+ ao_add_task(&ao_lco_monitor_task, ao_lco_main, "lco monitor");
+#if DEBUG
+ ao_cmd_register(&ao_lco_cmds[0]);
+#endif
+}
--- /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; 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 crystal */
+
+#define AO_HSE 1
+#define AO_HSE_BYPASS 0
+
+#define AO_SYSCLK 72000000
+#define AO_HCLK 72000000
+#define AO_APB1CLK 36000000
+#define AO_APB2CLK 72000000
+#define AO_ADCCLK 12000000
+
+/* PLLMUL is 9, PLLXTPRE (pre divider) is 2, so the
+ * overall PLLCLK is 16 * 9/2 = 72MHz (used as SYSCLK)
+ *
+ * HCLK is SYSCLK / 1 (HPRE_DIV) = 72MHz (72MHz max)
+ * USB is PLLCLK / 1.5 (USBPRE)= 48MHz (must be 48MHz)
+ * APB2 is HCLK / 1 (PPRE2_DIV) = 72MHz (72MHz max)
+ * APB1 is HCLK / 2 (PPRE1_DIV) = 36MHz (36MHz max)
+ * ADC is APB2 / 6 (ADCPRE) = 12MHz (14MHz max)
+ */
+
+#define AO_RCC_CFGR_USBPRE STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE STM_RCC_CFGR_PLLXTPRE_2
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE1_DIV_2
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+#define AO_RCC_CFGR_ADCPRE STM_RCC_CFGR_ADCPRE_6
+
+
+#define AO_CC1200_FOSC 40000000
+
+#define HAS_EEPROM 1
+#define USE_INTERNAL_FLASH 1
+#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 1
+#define BEEPER_PORT (&stm_gpioc)
+#define BEEPER_PIN 6
+#define AO_BEEP_MID_DEFAULT 179 /* 2100 Hz */
+#define HAS_RADIO 1
+#define HAS_RADIO_RATE 1
+#define HAS_TELEMETRY 0
+#define HAS_AES 1
+#define HAS_STATIC_TEST 0
+
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT (&stm_gpioa)
+#define AO_USB_PULLUP_PIN 10
+
+#define HAS_SPI_1 1 /* NHD-C12864LZ LCD Module */
+#define SPI_1_PA5_PA6_PA7 1
+#define SPI_1_PA6_DISABLE 1
+#define SPI_1_MODE_OUTPUT STM_GPIO_CR_MODE_OUTPUT_50MHZ
+#define SPI_1_PB3_PB4_PB5 0
+#define SPI_1_PE13_PE14_PE15 0
+
+#define HAS_SPI_2 1 /* CC1200 */
+#define SPI_2_PB13_PB14_PB15 1
+#define SPI_2_PD1_PD3_PD4 0
+#define SPI_2_GPIO (&stm_gpiod)
+#define SPI_2_SCK 1
+#define SPI_2_MISO 3
+#define SPI_2_MOSI 4
+#define SPI_2_MODE_OUTPUT STM_GPIO_CR_MODE_OUTPUT_10MHZ
+
+#define HAS_I2C_1 0
+
+#define HAS_I2C_2 0
+
+#define PACKET_HAS_SLAVE 0
+#define PACKET_HAS_MASTER 0
+
+#define AO_FAST_TIMER 4
+#define FAST_TIMER_FREQ 10000 /* .1ms for debouncing */
+
+/* LCD module */
+#define AO_ST7565_CS_PORT (&stm_gpioc) /* pin 1 */
+#define AO_ST7565_CS_PIN 4
+#define AO_ST7565_RESET_PORT (&stm_gpioc) /* pin 2 */
+#define AO_ST7565_RESET_PIN 5
+#define AO_ST7565_A0_PORT (&stm_gpioa) /* pin 3 */
+#define AO_ST7565_A0_PIN 3
+#define AO_ST7565_SPI_BUS (AO_SPI_1_PA5_PA6_PA7 | AO_SPI_MODE_3)
+#define AO_ST7565_WIDTH 128
+#define AO_ST7565_HEIGHT 64
+#define AO_ST7565_BIAS ST7565_LCD_BIAS_1_9
+
+/*
+ * Radio is a cc1200 connected via SPI
+ */
+
+#define AO_RADIO_CAL_DEFAULT 5695733
+
+#define AO_CC1200_SPI_CS_PORT (&stm_gpiob)
+#define AO_CC1200_SPI_CS_PIN 8
+#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 (9)
+
+#define AO_CC1200_INT_GPIO 2
+#define AO_CC1200_INT_GPIO_IOCFG CC1200_IOCFG2
+
+#define LOW_LEVEL_DEBUG 0
+
+#define HAS_LED 1
+
+#define AO_LED_RED AO_LED_0 /* PC7 */
+#define LED_0_PORT (&stm_gpioc)
+#define LED_0_PIN 7
+
+#define AO_LED_AMBER AO_LED_1 /* PC8 */
+#define LED_1_PORT (&stm_gpioc)
+#define LED_1_PIN 8
+
+#define AO_LED_GREEN AO_LED_2 /* PC9 */
+#define LED_2_PORT (&stm_gpioc)
+#define LED_2_PIN 9
+
+#define AO_LED_BOX AO_LED_3 /* PA9 */
+#define LED_3_PORT (&stm_gpioa)
+#define LED_3_PIN 9
+
+#define AO_LED_PAD AO_LED_4 /* PA15 */
+#define LED_4_PORT (&stm_gpioa)
+#define LED_4_PIN 15
+
+#define AO_LED_DRAG AO_LED_5 /* PC12 */
+#define LED_5_PORT (&stm_gpioc)
+#define LED_5_PIN 12
+
+#define AO_LED_CONTINUITY_7 AO_LED_6 /* PC13 */
+#define LED_6_PORT (&stm_gpioc)
+#define LED_6_PIN 13
+
+#define AO_LED_CONTINUITY_6 AO_LED_7 /* PC14 */
+#define LED_7_PORT (&stm_gpioc)
+#define LED_7_PIN 14
+
+#define AO_LED_CONTINUITY_5 AO_LED_8 /* PC15 */
+#define LED_8_PORT (&stm_gpioc)
+#define LED_8_PIN 15
+
+#define AO_LED_CONTINUITY_4 AO_LED_9 /* PC2 */
+#define LED_9_PORT (&stm_gpioc)
+#define LED_9_PIN 2
+
+#define AO_LED_CONTINUITY_3 AO_LED_10 /* PC3 */
+#define LED_10_PORT (&stm_gpioc)
+#define LED_10_PIN 3
+
+#define AO_LED_CONTINUITY_2 AO_LED_11 /* PA0 */
+#define LED_11_PORT (&stm_gpioa)
+#define LED_11_PIN 0
+
+#define AO_LED_CONTINUITY_1 AO_LED_12 /* PA6 */
+#define LED_12_PORT (&stm_gpioa)
+#define LED_12_PIN 6
+
+#define AO_LED_CONTINUITY_0 AO_LED_13 /* PB1 */
+#define LED_13_PORT (&stm_gpiob)
+#define LED_13_PIN 1
+
+#define AO_LED_CONTINUITY_NUM 8
+
+#define AO_LED_REMOTE_ARM AO_LED_14 /* PB3 */
+#define LED_14_PORT (&stm_gpiob)
+#define LED_14_PIN 3
+
+#define AO_LED_FIRE AO_LED_15 /* PB0 */
+#define LED_15_PORT (&stm_gpiob)
+#define LED_15_PIN 0
+
+/*
+ * Use event queue for input devices
+ */
+
+#define AO_EVENT 1
+
+/*
+ * Knobs
+ */
+
+#define AO_QUADRATURE_COUNT 1
+#define AO_QUADRATURE_DEBOUNCE 0
+#define AO_QUADRATURE_SINGLE_CODE 1
+
+#define AO_QUADRATURE_0_PORT &stm_gpiob
+#define AO_QUADRATURE_0_A 12
+#define AO_QUADRATURE_0_B 11
+
+#define AO_QUADRATURE_SELECT 0
+
+/*
+ * Buttons
+ */
+
+#define AO_BUTTON_COUNT 9
+#define AO_BUTTON_MODE AO_EXTI_MODE_PULL_UP
+
+#define AO_BUTTON_DRAG_MODE 0
+#define AO_BUTTON_0_PORT &stm_gpioc
+#define AO_BUTTON_0 1
+
+#define AO_BUTTON_DRAG_SELECT 1
+#define AO_BUTTON_1_PORT &stm_gpioc
+#define AO_BUTTON_1 0
+
+#define AO_BUTTON_SPARE1 2
+#define AO_BUTTON_2_PORT &stm_gpiob
+#define AO_BUTTON_2 4
+
+#define AO_BUTTON_SPARE2 3
+#define AO_BUTTON_3_PORT &stm_gpiob
+#define AO_BUTTON_3 5
+
+#define AO_BUTTON_SPARE3 4
+#define AO_BUTTON_4_PORT &stm_gpiob
+#define AO_BUTTON_4 6
+
+#define AO_BUTTON_ARM 5
+#define AO_BUTTON_5_PORT &stm_gpioa
+#define AO_BUTTON_5 8
+
+#define AO_BUTTON_FIRE 6
+#define AO_BUTTON_6_PORT &stm_gpioa
+#define AO_BUTTON_6 4
+
+#define AO_BUTTON_SPARE4 7
+#define AO_BUTTON_7_PORT &stm_gpiob
+#define AO_BUTTON_7 7
+
+#define AO_BUTTON_ENCODER_SELECT 8
+#define AO_BUTTON_8_PORT &stm_gpiob
+#define AO_BUTTON_8 10
+
+/* ADC */
+
+struct ao_adc {
+ int16_t v_batt;
+};
+
+#define AO_ADC_DUMP(p) \
+ printf("batt: %5d\n", (p)->v_batt)
+
+#define HAS_ADC_SINGLE 1
+#define HAS_ADC_TEMP 0
+#define HAS_BATTERY_REPORT 1
+
+#define AO_ADC_V_BATT 2
+#define AO_ADC_V_BATT_PORT (&stm_gpioa)
+#define AO_ADC_V_BATT_PIN 2
+
+#define AO_ADC_PIN0_PORT AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN0_PIN AO_ADC_V_BATT_PIN
+
+#define AO_ADC_SQ1 AO_ADC_V_BATT
+
+#define AO_NUM_ADC 1
+
+/*
+ * Voltage divider on ADC battery sampler
+ */
+#define AO_BATTERY_DIV_PLUS 15 /* 15k */
+#define AO_BATTERY_DIV_MINUS 27 /* 27k */
+
+/*
+ * ADC reference in decivolts
+ */
+#define AO_ADC_REFERENCE_DV 33
+
+#define AO_LCO_SEARCH_API
+#define AO_LCO_HAS_CONTRAST 1
+#define AO_LCO_MIN_CONTRAST 0
+#define AO_LCO_MAX_CONTRAST 63
+#define AO_LCO_CONTRAST_STEP 1
+
+#define AO_LCO_HAS_BACKLIGHT 1
+#define AO_LCO_MIN_BACKLIGHT 0
+#define AO_LCO_MAX_BACKLIGHT 65535
+#define AO_LCO_BACKLIGHT_STEP 771
+
+#define AO_LCO_HAS_INFO 1
+#define AO_LCO_MIN_INFO_PAGE 0
+#define AO_LCO_MAX_INFO_PAGE 0
+
+/*
+ * LCD Backlight via PWM.
+ *
+ * Pin PA1, TIM2_CH2
+ */
+
+#define NUM_PWM 1
+#define PWM_MAX 65535
+#define AO_PWM_TIMER stm_tim2
+#define AO_LCD_BL_PWM_CHAN 1
+#define AO_PWM_0_GPIO (&stm_gpioa)
+#define AO_PWM_0_PIN 1
+#define AO_PWM_TIMER_ENABLE STM_RCC_APB1ENR_TIM2EN
+#define AO_PWM_TIMER_SCALE 1
+
+#define AO_AFIO_PWM_REMAP STM_AFIO_MAPR_TIM2_REMAP
+#define AO_AFIO_PWM_REMAP_VAL STM_AFIO_MAPR_TIM2_REMAP_PA0_PA1_PA2_PA3
+#define AO_AFIO_PWM_REMAP_MASK STM_AFIO_MAPR_TIM2_REMAP_MASK
+
+
+#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; 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_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_aes.h>
+#include <ao_quadrature.h>
+#include <ao_button.h>
+#include <ao_lco.h>
+#include <ao_lco_cmd.h>
+#include <ao_radio_cmac_cmd.h>
+#include <ao_eeprom.h>
+#include <ao_adc_single.h>
+#include <ao_st7565.h>
+#include <ao_pwm.h>
+
+#define WIDTH AO_ST7565_WIDTH
+#define HEIGHT AO_ST7565_HEIGHT
+#define STRIDE AO_BITMAP_STRIDE(WIDTH)
+
+static uint32_t image[STRIDE * HEIGHT];
+
+static struct ao_bitmap fb = {
+ .base = image,
+ .stride = STRIDE,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .damage = AO_BOX_INIT,
+};
+
+static void
+ao_st7565_test(void)
+{
+ ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+ ao_st7565_update(&fb);
+ ao_text(&fb, &BitstreamVeraSans_Roman_24_font,
+ 0, 20, "hello world", AO_BLACK, AO_COPY);
+ ao_st7565_update(&fb);
+}
+
+static int16_t x1 = 32, _y1 = 10, x2 = 32, y2 = 40;
+static int16_t dx1 = 2, dy1 = 2, dx2 = -2, dy2 = -1;
+
+#define bounds(v,m,M,d) \
+ if (v < m) { \
+ v = m + m - v; \
+ d = -d; \
+ } else if (v > M) { \
+ v = M - (v - M); \
+ d = -d; \
+ }
+
+static void
+ao_st7565_line(void)
+{
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+ ao_line(&fb, x1, _y1, x2, y2, AO_BLACK, AO_COPY);
+ ao_st7565_update(&fb);
+ x1 += dx1;
+ _y1 += dy1;
+ x2 += dx2;
+ y2 += dy2;
+ printf("%d,%d - %d,%d\n", x1, _y1, x2, y2);
+ fflush(stdout);
+ bounds(x1, 0, WIDTH, dx1);
+ bounds(x2, 0, WIDTH, dx2);
+ bounds(_y1, 0, HEIGHT, dy1);
+ bounds(y2, 0, HEIGHT, dy2);
+ ao_delay(AO_MS_TO_TICKS(200));
+ }
+}
+
+static const float pad_volts = 12.3f;
+static const float lco_volts = 4.1f;
+static const int rssi = -30;
+
+static int boxes[] = { 1, 2, 3, 5, 8, 11, 13, 17, 19, 23, 29, 31, 37, 62, 97 };
+
+//static int max_box = 97;
+
+#define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+static bool
+valid_box(int box)
+{
+ size_t i;
+ if (box == 0)
+ return true;
+ for (i = 0; i < ARRAYSIZE(boxes); i++)
+ if (boxes[i] == box)
+ return true;
+ return false;
+}
+
+#if 0
+static void
+next_box(void)
+{
+ for (int n = box_number + 1; n <= max_box; n++)
+ if (valid_box(n)) {
+ box_number = n;
+ return;
+ }
+ box_number = 0;
+}
+
+static void
+prev_box(void)
+{
+ for (int n = box_number - 1; n >= 0; n--)
+ if (valid_box(n)) {
+ box_number = n;
+ return;
+ }
+ box_number = max_box;
+}
+#endif
+
+static const struct ao_transform logo_transform = {
+ .x_scale = 48, .x_off = 2,
+ .y_scale = 48, .y_off = 0,
+};
+
+#define BIG_FONT BitstreamVeraSans_Roman_58_font
+#define VOLT_FONT BitstreamVeraSans_Roman_58_font
+#define SMALL_FONT BitstreamVeraSans_Roman_12_font
+#define TINY_FONT BitstreamVeraSans_Roman_10_font
+#define LOGO_FONT BenguiatGothicStd_Bold_26_font
+
+#define LABEL_Y (int16_t) (SMALL_FONT.ascent)
+#define VALUE_Y (int16_t) (LABEL_Y + BIG_FONT.ascent + 5)
+#define BOX_X 2
+#define PAD_X 90
+#define BOX_LABEL_X 30
+#define VOLT_LABEL_X 25
+#define RSSI_LABEL_X 15
+#define PAD_LABEL_X 95
+#define SEP_X (PAD_X - 8)
+#define SCAN_X (WIDTH - 100) / 2
+#define SCAN_Y 50
+#define SCAN_HEIGHT 3
+#define FOUND_Y 63
+#define FOUND_X 6
+#define FOUND_WIDTH 17
+#define MAX_VALID (WIDTH / FOUND_WIDTH)
+
+static int16_t box_number = 88;
+static int16_t pad_number = 8;
+
+static void
+ao_st7565_poly(void)
+{
+ int16_t scan_number;
+ char str[8];
+ int i;
+ int v;
+ int last_box;
+ int16_t b;
+
+ for (scan_number = 0; scan_number < 100; scan_number++) {
+ ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+ ao_logo(&fb, &logo_transform, &LOGO_FONT, AO_BLACK, AO_COPY);
+ if (scan_number) {
+ ao_rect(&fb, SCAN_X, SCAN_Y, (int16_t) scan_number, SCAN_HEIGHT, AO_BLACK, AO_COPY);
+ b = 0;
+ v = 0;
+ last_box = 0;
+ for (i = scan_number; i > 1; i--) {
+ if (valid_box(i)) {
+ if (!last_box)
+ last_box = i;
+ v++;
+ if (v == MAX_VALID)
+ break;
+ }
+ }
+ for (; i <= scan_number; i++) {
+ if (valid_box(i)) {
+ sprintf(str, "%02d%s", i, i == last_box ? "" : ",");
+ ao_text(&fb, &TINY_FONT, (int16_t) (FOUND_X + FOUND_WIDTH * b),
+ FOUND_Y, str, AO_BLACK, AO_COPY);
+ b++;
+ }
+ }
+ }
+ ao_st7565_update(&fb);
+ ao_delay(AO_MS_TO_TICKS(50));
+ }
+ ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+ switch (box_number) {
+ case 0:
+ sprintf(str, "%4.1f", lco_volts);
+ ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+ ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, "LCO Battery", AO_BLACK, AO_COPY);
+ break;
+ default:
+ switch (pad_number) {
+ case -1:
+ sprintf(str, "%4.1f", pad_volts);
+ ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+ ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, "Pad Battery", AO_BLACK, AO_COPY);
+ break;
+ case 0:
+ sprintf(str, "%4d", rssi);
+ ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+ ao_text(&fb, &SMALL_FONT, RSSI_LABEL_X, LABEL_Y, "Signal Strength", AO_BLACK, AO_COPY);
+ break;
+ default:
+ sprintf(str, "%02d", box_number);
+ ao_text(&fb, &BIG_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+ ao_text(&fb, &SMALL_FONT, BOX_LABEL_X, LABEL_Y, "Box", AO_BLACK, AO_COPY);
+
+ sprintf(str, "%d", pad_number);
+ ao_text(&fb, &BIG_FONT, PAD_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+ ao_text(&fb, &SMALL_FONT, PAD_LABEL_X, LABEL_Y, "Pad", AO_BLACK, AO_COPY);
+
+ ao_rect(&fb, SEP_X, 0, 2, HEIGHT, AO_BLACK, AO_COPY);
+ }
+ break;
+ }
+ ao_st7565_update(&fb);
+}
+
+const struct ao_cmds ao_st7565_cmds[] = {
+ { ao_st7565_test, "g\0Test ST7565 display" },
+ { ao_st7565_line, "l\0Draw lines" },
+ { ao_st7565_poly, "p\0Draw polygon" },
+ { 0, NULL },
+};
+
+int
+main(void)
+{
+ ao_clock_init();
+
+ ao_led_init();
+ ao_task_init();
+
+ ao_timer_init();
+
+ ao_spi_init();
+ ao_dma_init();
+ ao_exti_init();
+ ao_adc_single_init();
+
+ ao_beep_init();
+ ao_pwm_init();
+ ao_cmd_init();
+
+ ao_quadrature_init();
+ ao_button_init();
+
+ ao_radio_init();
+
+ ao_usb_init();
+
+ ao_st7565_init();
+
+ ao_config_init();
+
+ ao_lco_init();
+ ao_lco_cmd_init();
+
+// ao_cmd_register(ao_st7565_cmds);
+
+ ao_start_scheduler();
+ return 0;
+}
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telelco-v3.0
+include $(TOPDIR)/stm32f1/Makefile-flash.defs
--- /dev/null
+/*
+ * Copyright © 2018 Bdale Garbee <bdale@gag.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 crystal */
+
+#define AO_HSE 1
+#define AO_HSE_BYPASS 0
+
+#define AO_SYSCLK 72000000
+#define AO_HCLK 72000000
+#define AO_APB1CLK 36000000
+#define AO_APB2CLK 72000000
+#define AO_ADCCLK 12000000
+
+/* PLLMUL is 9, PLLXTPRE (pre divider) is 2, so the
+ * overall PLLCLK is 16 * 9/2 = 72MHz (used as SYSCLK)
+ *
+ * HCLK is SYSCLK / 1 (HPRE_DIV) = 72MHz (72MHz max)
+ * USB is PLLCLK / 1.5 (USBPRE)= 48MHz (must be 48MHz)
+ * APB2 is HCLK / 1 (PPRE2_DIV) = 72MHz (72MHz max)
+ * APB1 is HCLK / 2 (PPRE1_DIV) = 36MHz (36MHz max)
+ * ADC is APB2 / 6 (ADCPRE) = 12MHz (14MHz max)
+ */
+
+#define AO_RCC_CFGR_USBPRE STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE STM_RCC_CFGR_PLLXTPRE_2
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE1_DIV_2
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+#define AO_RCC_CFGR_ADCPRE STM_RCC_CFGR_ADCPRE_6
+
+#include <ao_flash_stm_pins.h>
+
+/* Companion port cs_companion0 PC10 */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpioc
+#define AO_BOOT_APPLICATION_PIN 10
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP
+
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT (&stm_gpioa)
+#define AO_USB_PULLUP_PIN 10
+
+#endif /* _AO_PINS_H_ */
return ok;
}
+int
+ao_hello_packet(void)
+{
+ uint8_t message[5] = "hello";
+ uint8_t encode[ENCODE_LEN(sizeof(message))];
+ int encode_len;
+ uint8_t transmit[EXPAND_LEN(sizeof(message))];
+ uint8_t decode[DECODE_LEN(sizeof(message))];
+ int transmit_len;
+ int decode_ok;
+
+ printf("Hello packet test:\n");
+ ao_fec_dump_bytes(message, sizeof(message), "Message");
+ encode_len = ao_fec_encode(message, sizeof(message), encode);
+ ao_fec_dump_bytes(encode, encode_len, "Encode");
+ transmit_len = ao_expand(encode, encode_len, transmit);
+ ao_fec_dump_bytes(transmit, transmit_len, "Transmit");
+ decode_ok = ao_fec_decode(transmit, transmit_len, decode, sizeof(message) + 2, NULL);
+ ao_fec_dump_bytes(decode, sizeof(message) + 2, "Receive");
+ printf("Hello result: %s\n", decode_ok ? "success" : "fail");
+ return decode_ok;
+}
+
#define EXPECT_DECODE_FAIL 0
-#define EXPECT_CRC_MISMATCH 6386
+#define EXPECT_CRC_MISMATCH 6304
#define EXPECT_DATA_MISMATCH 0
#define NOISE_AMOUNT 0x50
if (!ao_real_packet())
errors++;
+ if (!ao_hello_packet())
+ errors++;
+
srandom(0);
for (trial = 0; trial < 100000; trial++) {
JLabel radio_enable_label;
JLabel radio_10mw_label;
JLabel report_feet_label;
+ JLabel gps_receiver_label;
JLabel rate_label;
JLabel aprs_interval_label;
JLabel aprs_ssid_label;
JRadioButton radio_enable_value;
JRadioButton radio_10mw_value;
JComboBox<String> report_feet_value;
+ JComboBox<String> gps_receiver_value;
AltosUIRateList rate_value;
JComboBox<String> aprs_interval_value;
JComboBox<Integer> aprs_ssid_value;
return AltosLib.MISSING;
}
+ void set_gps_receiver_tool_tip() {
+ if (gps_receiver_value.isVisible())
+ gps_receiver_value.setToolTipText("GPS receiver selection");
+ else
+ gps_receiver_value.setToolTipText("Only TeleMega with new firmware supports alternate GPS receivers");
+ }
+
+ public void set_gps_receiver(int new_gps_receiver) {
+ System.out.printf("set_gps_receiver %d\n", new_gps_receiver);
+ if (new_gps_receiver != AltosLib.MISSING) {
+ if (new_gps_receiver >= AltosLib.gps_receiver_names.length)
+ new_gps_receiver = 0;
+ if (new_gps_receiver < 0) {
+ gps_receiver_value.setEnabled(false);
+ new_gps_receiver = 0;
+ } else {
+ gps_receiver_value.setEnabled(true);
+ }
+ gps_receiver_value.setSelectedIndex(new_gps_receiver);
+ }
+ gps_receiver_value.setVisible(new_gps_receiver != AltosLib.MISSING);
+ gps_receiver_label.setVisible(new_gps_receiver != AltosLib.MISSING);
+
+ set_gps_receiver_tool_tip();
+ }
+
+ public int gps_receiver() {
+ if (gps_receiver_value.isVisible())
+ return gps_receiver_value.getSelectedIndex();
+ else
+ return AltosLib.MISSING;
+ }
+
void set_rate_tool_tip() {
if (rate_value.isVisible())
rate_value.setToolTipText("Select telemetry baud rate");
flight_log_max_value.setToolTipText("Cannot set max value with flight logs in memory");
}
+ public boolean has_radio() { return true; }
+
/* Build the UI using a grid bag */
public TeleGPSConfigUI(JFrame in_owner) {
super (in_owner, "Configure Device", false);
set_report_feet_tool_tip();
row++;
+ /* GPS Receiver */
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = row;
+ c.gridwidth = 4;
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ c.ipady = 5;
+ gps_receiver_label = new JLabel("GPS Receiver:");
+ pane.add(gps_receiver_label, c);
+
+ c = new GridBagConstraints();
+ c.gridx = 4; c.gridy = row;
+ c.gridwidth = 4;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ c.ipady = 5;
+ gps_receiver_value = new JComboBox<String>(AltosLib.gps_receiver_names);
+ gps_receiver_value.setEditable(false);
+ gps_receiver_value.addItemListener(this);
+ pane.add(gps_receiver_value, c);
+ set_gps_receiver_tool_tip();
+ row++;
+
/* Radio 10mW limit */
c = new GridBagConstraints();
c.gridx = 0; c.gridy = row;