Merge branch 'master' into branch-1.9
authorBdale Garbee <bdale@gag.com>
Fri, 20 Jan 2023 05:55:56 +0000 (22:55 -0700)
committerBdale Garbee <bdale@gag.com>
Fri, 20 Jan 2023 05:55:56 +0000 (22:55 -0700)
151 files changed:
Releasing
altoslib/AltosCSV.java
altoslib/AltosConfigData.java
altoslib/AltosConfigValues.java
altoslib/AltosEepromRecordMega.java
altoslib/AltosEepromRecordSet.java
altoslib/AltosIMU.java
altoslib/AltosIdleFetch.java
altoslib/AltosLib.java
altoslib/AltosTelemetry.java
altoslib/AltosTelemetryStandard.java
altoslib/NewProduct
altosui/AltosConfigFCUI.java
altosui/Makefile.am
altosui/altos-windows.nsi.in
ao-bringup/test-easymini-v3.0 [new file with mode: 0755]
ao-bringup/test-easymotor [deleted file]
ao-bringup/test-easymotor-v3 [new file with mode: 0755]
ao-bringup/turnon_easymini
ao-bringup/turnon_easymini_v1.0 [new file with mode: 0755]
ao-bringup/turnon_easymotor
ao-tools/Makefile.am
ao-tools/ao-eeprom/ao-eeprom.c
ao-tools/ao-flash/Makefile.am
ao-tools/ao-flash/ao-flash-lpc
ao-tools/ao-flash/ao-flash-samd21 [new file with mode: 0755]
ao-tools/ao-flash/ao-flash-samd21.1 [new file with mode: 0644]
ao-tools/ao-test-pressure/Makefile.am [new file with mode: 0644]
ao-tools/ao-test-pressure/ao-test-pressure.1 [new file with mode: 0644]
ao-tools/ao-test-pressure/ao-test-pressure.c [new file with mode: 0644]
ao-tools/lib/ao-eeprom-read.h
ao-tools/lib/ao-hex.c
ao-tools/lib/cc-usb.c
configure.ac
doc/Makefile.am
doc/altos.txt
doc/altusmetrum-theme.yml
doc/altusmetrum.txt
doc/companion.txt
doc/easymini-release-notes.inc
doc/easymini.txt
doc/header.inc
doc/map-loading.txt
doc/micropeak.txt
doc/motortest-installation.inc
doc/motortest.txt
doc/release-head.inc
doc/release-notes-1.9.13.inc [new file with mode: 0644]
doc/release-notes.inc
doc/telegps-release-notes.inc
doc/telegps.txt
doc/telelaunch-operation.inc
doc/telelaunch-specs.inc
doc/telelaunch.txt
doc/telemetry.txt
libaltos/libaltos.h
libaltos/libaltos.i0
src/Makefile
src/Makefile.defs
src/attiny/ao_arch_funcs.h
src/drivers/ao_aprs.c
src/drivers/ao_bmi088.c [new file with mode: 0644]
src/drivers/ao_bmi088.h [new file with mode: 0644]
src/drivers/ao_cc1200.c
src/drivers/ao_gps_skytraq.c
src/drivers/ao_gps_ublox.c
src/drivers/ao_lco.h
src/drivers/ao_lco_bits.c
src/drivers/ao_led.c
src/drivers/ao_ms5607.c
src/easymotor-v3/.gitignore [new file with mode: 0644]
src/easymotor-v3/Makefile
src/easymotor-v3/ao_pins.h
src/kernel/ao.h
src/kernel/ao_config.c
src/kernel/ao_config.h
src/kernel/ao_data.h
src/kernel/ao_log.h
src/kernel/ao_log_mega.c
src/kernel/ao_motor_flight.c [new file with mode: 0644]
src/kernel/ao_report.c
src/kernel/ao_sample.c
src/kernel/ao_task.c
src/kernel/ao_telemetry.c
src/kernel/ao_telemetry.h
src/metro-m0/ao_pins.h [new file with mode: 0644]
src/metro-m0/flash-loader/ao_pins.h [new file with mode: 0644]
src/metro-m0/metro-m0.c [new file with mode: 0644]
src/nucleao-32/Makefile
src/nucleao-32/ao_nucleo.c
src/nucleao-32/ao_pins.h
src/samd21/Makefile-flash.defs [new file with mode: 0644]
src/samd21/Makefile-samd21.defs [new file with mode: 0644]
src/samd21/Makefile.defs [new file with mode: 0644]
src/samd21/altos-128-16.ld [new file with mode: 0644]
src/samd21/altos-256-32.ld [new file with mode: 0644]
src/samd21/altos-loader.ld [new file with mode: 0644]
src/samd21/ao_adc_samd21.c [new file with mode: 0644]
src/samd21/ao_adc_samd21.h [new file with mode: 0644]
src/samd21/ao_apa102.c [new file with mode: 0644]
src/samd21/ao_arch.h [new file with mode: 0644]
src/samd21/ao_arch_funcs.h [new file with mode: 0644]
src/samd21/ao_beep_samd21.c [new file with mode: 0644]
src/samd21/ao_boot_chain.c [new file with mode: 0644]
src/samd21/ao_boot_pin.c [new file with mode: 0644]
src/samd21/ao_dac_samd21.c [new file with mode: 0644]
src/samd21/ao_dac_samd21.h [new file with mode: 0644]
src/samd21/ao_dma_samd21.c [new file with mode: 0644]
src/samd21/ao_dma_samd21.h [new file with mode: 0644]
src/samd21/ao_exti.h [new file with mode: 0644]
src/samd21/ao_exti_samd21.c [new file with mode: 0644]
src/samd21/ao_flash.h [new file with mode: 0644]
src/samd21/ao_flash_loader_samd21.c [new file with mode: 0644]
src/samd21/ao_flash_samd21.c [new file with mode: 0644]
src/samd21/ao_flash_samd21_pins.h [new file with mode: 0644]
src/samd21/ao_interrupt.c [new file with mode: 0644]
src/samd21/ao_neopixel.c [new file with mode: 0644]
src/samd21/ao_serial.h [new file with mode: 0644]
src/samd21/ao_serial_samd21.c [new file with mode: 0644]
src/samd21/ao_spi_samd21.c [new file with mode: 0644]
src/samd21/ao_tc_samd21.c [new file with mode: 0644]
src/samd21/ao_tc_samd21.h [new file with mode: 0644]
src/samd21/ao_tcc_samd21.c [new file with mode: 0644]
src/samd21/ao_tcc_samd21.h [new file with mode: 0644]
src/samd21/ao_timer.c [new file with mode: 0644]
src/samd21/ao_usb_samd21.c [new file with mode: 0644]
src/samd21/registers.ld [new file with mode: 0644]
src/samd21/samd21.h [new file with mode: 0644]
src/snekboard/Makefile [new file with mode: 0644]
src/snekboard/ao_pins.h [new file with mode: 0644]
src/snekboard/flash-loader/Makefile [new file with mode: 0644]
src/snekboard/flash-loader/ao_pins.h [new file with mode: 0644]
src/snekboard/snekboard.c [new file with mode: 0644]
src/stm/ao_arch_funcs.h
src/stm/ao_spi_stm.c
src/stm32l0/ao_arch_funcs.h
src/stmf0/ao_arch_funcs.h
src/stmf0/ao_spi_stm.c
src/telebt-v4.0/ao_pins.h
src/telelco-v2.0/ao_lco_v2.c
src/telemega-v6.0/Makefile [new file with mode: 0644]
src/telemega-v6.0/ao_pins.h [new file with mode: 0644]
src/telemega-v6.0/ao_telemega.c [new file with mode: 0644]
src/telemega-v6.0/flash-loader/Makefile [new file with mode: 0644]
src/telemega-v6.0/flash-loader/ao_pins.h [new file with mode: 0644]
src/telemetrum-v4.0/Makefile [new file with mode: 0644]
src/telemetrum-v4.0/ao_pins.h [new file with mode: 0644]
src/telemetrum-v4.0/ao_telemetrum.c [new file with mode: 0644]
src/telemetrum-v4.0/flash-loader/Makefile [new file with mode: 0644]
src/telemetrum-v4.0/flash-loader/ao_pins.h [new file with mode: 0644]
telegps/TeleGPSConfigUI.java

index f3ac998aacb565408398fee454c9def8c3936efa..939b10b71eb7872e01f8d8542e51fa1675d41c3c 100644 (file)
--- a/Releasing
+++ b/Releasing
@@ -82,6 +82,7 @@ These are Bdale's notes on how to do a release.
 
        - verify debian/changelog is "clean" ending in last release version
        - craft a suitable debian/changelog entry, possibly using:
+       export EMAIL=bdale@gag.com
        gbp dch --release --multimaint-merge --new-version=<version>-1 
 
         git commit -n debian/changelog -m "update changelog for Debian build"
@@ -118,21 +119,21 @@ These are Bdale's notes on how to do a release.
           src/teledongle-v3.0/{*.elf,*.ihx,*.map} \
           src/telegps-v[1-2].0/{*.elf,*.ihx,*.map} \
           src/telemega-v[1-5].0/{*.elf,*.ihx,*.map} \
-          src/telemetrum-v[2-3].0/{*.elf,*.ihx,*.map} \
+          src/telemetrum-v[2-4].0/{*.elf,*.ihx,*.map} \
           src/telemini-v3.0/{*.elf,*.ihx,*.map} \
           src/telelco-v2.0/{*.elf,*.ihx,*.map} \
           src/telefireeight-v[1-2].0/{*.elf,*.ihx,*.map} \
           ~/altusmetrumllc/Binaries/
        cp src/chaoskey-v1.0/flash-loader/{*.elf,*.bin,*.map} \
           src/easymega-v[1-2].0/flash-loader/*.elf \
-          src/easymini-v[1-2].0/flash-loader/*.elf \
-          src/easymotor-v2/flash-loader/*.elf \
+          src/easymini-v[1-3].0/flash-loader/*.elf \
+          src/easymotor-v3/flash-loader/*.elf \
           src/easytimer-v1/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-2].0/flash-loader/{*.elf,*.bin,*.map} \
           src/telemega-v[1-5].0/flash-loader/*.elf \
-          src/telemetrum-v[2-3].0/flash-loader/*.elf \
+          src/telemetrum-v[2-4].0/flash-loader/*.elf \
           src/telemini-v3.0/flash-loader/{*.elf,*.bin,*.map} \
           src/telelco-v2.0/flash-loader/*.elf \
           src/telefireeight-v[1-2].0/flash-loader/*.elf \
index 16c57ec187104e5f8449f3ba030d496f734bd894..ffa4e37c863abc15a3121c81b913ab896994ff7b 100644 (file)
@@ -132,10 +132,10 @@ public class AltosCSV implements AltosWriter {
         */
 
        void write_general_header() {
-               out.printf("version,serial,flight");
+               out.printf(Locale.ROOT,"version,serial,flight");
                if (series.cal_data().callsign != null)
-                       out.printf(",call");
-               out.printf(",time");
+                       out.printf(Locale.ROOT,",call");
+               out.printf(Locale.ROOT,",time");
        }
 
        double time() {
@@ -143,17 +143,17 @@ public class AltosCSV implements AltosWriter {
        }
 
        void write_general() {
-               out.printf("%s, %d, %d",
+               out.printf(Locale.ROOT,"%s, %d, %d",
                           ALTOS_CSV_VERSION,
                           series.cal_data().serial,
                           series.cal_data().flight);
                if (series.cal_data().callsign != null)
-                       out.printf(",%s", series.cal_data().callsign);
-               out.printf(", %8.2f", time());
+                       out.printf(Locale.ROOT,",%s", series.cal_data().callsign);
+               out.printf(Locale.ROOT,", %8.2f", time());
        }
 
        void write_radio_header() {
-               out.printf("rssi,lqi");
+               out.printf(Locale.ROOT,"rssi,lqi");
        }
 
        int rssi() {
@@ -165,12 +165,12 @@ public class AltosCSV implements AltosWriter {
        }
 
        void write_radio() {
-               out.printf("%4d, %3d",
+               out.printf(Locale.ROOT,"%4d, %3d",
                           rssi(), status() & 0x7f);
        }
 
        void write_flight_header() {
-               out.printf("state,state_name");
+               out.printf(Locale.ROOT,"state,state_name");
        }
 
        int state() {
@@ -179,19 +179,19 @@ public class AltosCSV implements AltosWriter {
 
        void write_flight() {
                int state = state();
-               out.printf("%2d,%8s", state, AltosLib.state_name(state));
+               out.printf(Locale.ROOT,"%2d,%8s", state, AltosLib.state_name(state));
        }
 
        void write_basic_header() {
                if (has_accel)
-                       out.printf("acceleration,");
+                       out.printf(Locale.ROOT,"acceleration,");
                if (has_baro)
-                       out.printf("pressure,altitude,");
-               out.printf("height,speed");
+                       out.printf(Locale.ROOT,"pressure,altitude,");
+               out.printf(Locale.ROOT,"height,speed");
                if (has_baro)
-                       out.printf(",temperature");
+                       out.printf(Locale.ROOT,",temperature");
                if (has_pyro)
-                       out.printf(",drogue_voltage,main_voltage");
+                       out.printf(Locale.ROOT,",drogue_voltage,main_voltage");
        }
 
        double acceleration() { return series.value(AltosFlightSeries.accel_name, indices); }
@@ -205,42 +205,42 @@ public class AltosCSV implements AltosWriter {
 
        void write_basic() {
                if (has_accel)
-                       out.printf("%8.2f,", acceleration());
+                       out.printf(Locale.ROOT,"%8.2f,", acceleration());
                if (has_baro)
-                       out.printf("%10.2f,%8.2f,",
+                       out.printf(Locale.ROOT,"%10.2f,%8.2f,",
                                   pressure(), altitude());
-               out.printf("%8.2f,%8.2f",
+               out.printf(Locale.ROOT,"%8.2f,%8.2f",
                           height(), speed());
                if (has_baro)
-                       out.printf(",%5.1f", temperature());
+                       out.printf(Locale.ROOT,",%5.1f", temperature());
                if (has_pyro)
-                       out.printf(",%5.2f,%5.2f",
+                       out.printf(Locale.ROOT,",%5.2f,%5.2f",
                                   apogee_voltage(),
                                   main_voltage());
        }
 
        void write_battery_header() {
-               out.printf("battery_voltage");
+               out.printf(Locale.ROOT,"battery_voltage");
        }
 
        double battery_voltage() { return series.value(AltosFlightSeries.battery_voltage_name, indices); }
 
        void write_battery() {
-               out.printf("%5.2f", battery_voltage());
+               out.printf(Locale.ROOT,"%5.2f", battery_voltage());
        }
 
        void write_motor_pressure_header() {
-               out.printf("motor_pressure");
+               out.printf(Locale.ROOT,"motor_pressure");
        }
 
        double motor_pressure() { return series.value(AltosFlightSeries.motor_pressure_name, indices); }
 
        void write_motor_pressure() {
-               out.printf("%10.1f", motor_pressure());
+               out.printf(Locale.ROOT,"%10.1f", motor_pressure());
        }
 
        void write_3d_accel_header() {
-               out.printf("accel_x,accel_y,accel_z");
+               out.printf(Locale.ROOT,"accel_x,accel_y,accel_z");
        }
 
        double accel_along() { return series.value(AltosFlightSeries.accel_along_name, indices); }
@@ -248,12 +248,12 @@ public class AltosCSV implements AltosWriter {
        double accel_through() { return series.value(AltosFlightSeries.accel_through_name, indices); }
 
        void write_3d_accel() {
-               out.printf("%7.2f,%7.2f,%7.2f",
+               out.printf(Locale.ROOT,"%7.2f,%7.2f,%7.2f",
                           accel_along(), accel_across(), accel_through());
        }
 
        void write_imu_header() {
-               out.printf("gyro_roll,gyro_pitch,gyro_yaw,mag_x,mag_y,mag_z,tilt");
+               out.printf(Locale.ROOT,"gyro_roll,gyro_pitch,gyro_yaw,mag_x,mag_y,mag_z,tilt");
        }
 
        double gyro_roll() { return series.value(AltosFlightSeries.gyro_roll_name, indices); }
@@ -267,16 +267,16 @@ public class AltosCSV implements AltosWriter {
        double tilt() { return series.value(AltosFlightSeries.orient_name, indices); }
 
        void write_imu() {
-               out.printf("%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f",
+               out.printf(Locale.ROOT,"%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f",
                           gyro_roll(), gyro_pitch(), gyro_yaw(),
                           mag_along(), mag_across(), mag_through(),
                           tilt());
        }
 
        void write_igniter_header() {
-               out.printf("pyro");
+               out.printf(Locale.ROOT,"pyro");
                for (int i = 0; i < series.igniter_voltage.length; i++)
-                       out.printf(",%s", AltosLib.igniter_short_name(i));
+                       out.printf(Locale.ROOT,",%s", AltosLib.igniter_short_name(i));
        }
 
        double pyro() { return series.value(AltosFlightSeries.pyro_voltage_name, indices); }
@@ -284,13 +284,13 @@ public class AltosCSV implements AltosWriter {
        double igniter_value(int channel) { return series.value(series.igniter_voltage_name(channel), indices); }
 
        void write_igniter() {
-               out.printf("%5.2f", pyro());
+               out.printf(Locale.ROOT,"%5.2f", pyro());
                for (int i = 0; i < series.igniter_voltage.length; i++)
-                       out.printf(",%5.2f", igniter_value(i));
+                       out.printf(Locale.ROOT,",%5.2f", igniter_value(i));
        }
 
        void write_gps_header() {
-               out.printf("connected,locked,nsat,latitude,longitude,altitude,year,month,day,hour,minute,second,pad_dist,pad_range,pad_az,pad_el,pdop,hdop,vdop");
+               out.printf(Locale.ROOT,"connected,locked,nsat,latitude,longitude,altitude,year,month,day,hour,minute,second,pad_dist,pad_range,pad_az,pad_el,pdop,hdop,vdop");
        }
 
        void write_gps() {
@@ -306,7 +306,7 @@ public class AltosCSV implements AltosWriter {
                if (gps == null)
                        gps = new AltosGPS();
 
-               out.printf("%2d,%2d,%3d,%12.7f,%12.7f,%8.1f,%5d,%3d,%3d,%3d,%3d,%3d,%9.0f,%9.0f,%4.0f,%4.0f,%6.1f,%6.1f,%6.1f",
+               out.printf(Locale.ROOT,"%2d,%2d,%3d,%12.7f,%12.7f,%8.1f,%5d,%3d,%3d,%3d,%3d,%3d,%9.0f,%9.0f,%4.0f,%4.0f,%6.1f,%6.1f,%6.1f",
                           gps.connected?1:0,
                           gps.locked?1:0,
                           gps.nsat,
@@ -330,9 +330,9 @@ public class AltosCSV implements AltosWriter {
 
        void write_gps_sat_header() {
                for(int i = 1; i <= 32; i++) {
-                       out.printf("sat%02d", i);
+                       out.printf(Locale.ROOT,"sat%02d", i);
                        if (i != 32)
-                               out.printf(",");
+                               out.printf(Locale.ROOT,",");
                }
        }
 
@@ -349,15 +349,15 @@ public class AltosCSV implements AltosWriter {
                        }
                        out.printf ("%3d", c_n0);
                        if (i != 32)
-                               out.printf(",");
+                               out.printf(Locale.ROOT,",");
                }
        }
 
        void write_companion_header() {
 /*
-               out.printf("companion_id,companion_time,companion_update,companion_channels");
+               out.printf(Locale.ROOT,"companion_id,companion_time,companion_update,companion_channels");
                for (int i = 0; i < 12; i++)
-                       out.printf(",companion_%02d", i);
+                       out.printf(Locale.ROOT,",companion_%02d", i);
 */
        }
 
@@ -367,65 +367,65 @@ public class AltosCSV implements AltosWriter {
 
                int     channels_written = 0;
                if (companion == null) {
-                       out.printf("0,0,0,0");
+                       out.printf(Locale.ROOT,"0,0,0,0");
                } else {
-                       out.printf("%3d,%5.2f,%5.2f,%2d",
+                       out.printf(Locale.ROOT,"%3d,%5.2f,%5.2f,%2d",
                                   companion.board_id,
                                   (companion.tick - boost_tick) / 100.0,
                                   companion.update_period / 100.0,
                                   companion.channels);
                        for (; channels_written < companion.channels; channels_written++)
-                               out.printf(",%5d", companion.companion_data[channels_written]);
+                               out.printf(Locale.ROOT,",%5d", companion.companion_data[channels_written]);
                }
                for (; channels_written < 12; channels_written++)
-                       out.printf(",0");
+                       out.printf(Locale.ROOT,",0");
 */
        }
 
        void write_header() {
-               out.printf("#"); write_general_header();
+               out.printf(Locale.ROOT,"#"); write_general_header();
                if (has_radio) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_radio_header();
                }
                if (has_flight_state) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_flight_header();
                }
                if (has_basic) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_basic_header();
                }
                if (has_battery) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_battery_header();
                }
                if (has_motor_pressure) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_motor_pressure_header();
                }
                if (has_3d_accel) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_3d_accel_header();
                }
                if (has_imu) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_imu_header();
                }
                if (has_igniter) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_igniter_header();
                }
                if (has_gps) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_gps_header();
                }
                if (has_gps_sat) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_gps_sat_header();
                }
                if (has_companion) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_companion_header();
                }
                out.printf ("\n");
@@ -434,47 +434,47 @@ public class AltosCSV implements AltosWriter {
        void write_one() {
                write_general();
                if (has_radio) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_radio();
                }
                if (has_flight_state) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_flight();
                }
                if (has_basic) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_basic();
                }
                if (has_battery) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_battery();
                }
                if (has_motor_pressure) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_motor_pressure();
                }
                if (has_3d_accel) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_3d_accel();
                }
                if (has_imu) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_imu();
                }
                if (has_igniter) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_igniter();
                }
                if (has_gps) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_gps();
                }
                if (has_gps_sat) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_gps_sat();
                }
                if (has_companion) {
-                       out.printf(",");
+                       out.printf(Locale.ROOT,",");
                        write_companion();
                }
                out.printf ("\n");
index da6e7bfc6d8199b228e0b8918219c3fafb6038b2..ce34db7fd67ccc06b01dcb9a25c61dadba3421dc 100644 (file)
@@ -86,6 +86,8 @@ public class AltosConfigData {
        /* HAS_RADIO_10MW */
        public int              radio_10mw;
 
+       public int              report_feet;
+
        /* Storage info replies */
        public int      storage_size;
        public int      storage_erase_unit;
@@ -215,8 +217,11 @@ public class AltosConfigData {
                                return 4095 - value;
                        /* fall through */
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
+               case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+               case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
                case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
                case AltosLib.AO_LOG_FORMAT_EASYMOTOR:
+                       /* ADXL375 */
                        return -value;
                default:
                        if (product.startsWith("EasyTimer-"))
@@ -320,6 +325,8 @@ public class AltosConfigData {
 
                radio_10mw = AltosLib.MISSING;
 
+               report_feet = AltosLib.MISSING;
+
                tracker_motion = AltosLib.MISSING;
                tracker_interval = AltosLib.MISSING;
 
@@ -513,6 +520,8 @@ public class AltosConfigData {
                /* HAS_RADIO_10MW */
                try { radio_10mw = get_int(line, "Radio 10mw limit:"); } catch (Exception e) {}
 
+               try { report_feet = get_int(line, "Report in feet:"); } catch (Exception e) {}
+
                /* HAS_TRACKER */
                try {
                        int[] values = get_values(line, "Tracker setting:");
@@ -632,6 +641,8 @@ public class AltosConfigData {
                        return false;
                if (product.startsWith("TeleMetrum-v3"))
                        return false;
+               if (product.startsWith("TeleMetrum-v4"))
+                       return true;
                if (product.startsWith("EasyMega"))
                        return false;
                return true;
@@ -658,8 +669,14 @@ public class AltosConfigData {
                                return true;
                        if (product.startsWith("TeleMetrum-v3"))
                                return true;
+                       if (product.startsWith("TeleMetrum-v4"))
+                               return true;
                        if (product.startsWith("TeleMega-v4"))
                                return true;
+                       if (product.startsWith("TeleMega-v5"))
+                               return true;
+                       if (product.startsWith("TeleMega-v6"))
+                               return true;
                        if (product.startsWith("EasyMotor-v2"))
                                return true;
                        if (product.startsWith("EasyMotor-v3"))
@@ -674,8 +691,14 @@ public class AltosConfigData {
                                return AltosAdxl375.X_AXIS;
                        if (product.startsWith("TeleMetrum-v3"))
                                return AltosAdxl375.X_AXIS;
+                       if (product.startsWith("TeleMetrum-v4"))
+                               return AltosAdxl375.X_AXIS;
                        if (product.startsWith("TeleMega-v4"))
                                return AltosAdxl375.X_AXIS;
+                       if (product.startsWith("TeleMega-v5"))
+                               return AltosAdxl375.X_AXIS;
+                       if (product.startsWith("TeleMega-v6"))
+                               return AltosAdxl375.X_AXIS;
                        if (product.startsWith("EasyMotor-v2"))
                                return AltosAdxl375.X_AXIS;
                        if (product.startsWith("EasyMotor-v3"))
@@ -747,6 +770,9 @@ public class AltosConfigData {
                if (radio_10mw != AltosLib.MISSING)
                        radio_10mw = source.radio_10mw();
 
+               if (report_feet != AltosLib.MISSING)
+                       report_feet = source.report_feet();
+
                /* HAS_TRACKER */
                if (tracker_motion != AltosLib.MISSING)
                        tracker_motion = source.tracker_motion();
@@ -803,6 +829,7 @@ public class AltosConfigData {
                dest.set_aprs_offset(aprs_offset);
                dest.set_beep(beep);
                dest.set_radio_10mw(radio_10mw);
+               dest.set_report_feet(report_feet);
                dest.set_tracker_motion(tracker_motion);
                dest.set_tracker_interval(tracker_interval);
        }
@@ -926,6 +953,10 @@ public class AltosConfigData {
                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_TRACKER */
                if (tracker_motion != AltosLib.MISSING && tracker_interval != AltosLib.MISSING)
                        link.printf("c t %d %d\n", tracker_motion, tracker_interval);
index 65b5f0018de5136fbc48b003c5ff52dd0ded9cf3..8fb302779d188892d541c211ddaca607754fa685 100644 (file)
@@ -123,4 +123,8 @@ public interface AltosConfigValues {
        public abstract int radio_10mw() throws AltosConfigDataException;
 
        public abstract void set_radio_10mw(int radio_10mw);
+
+       public abstract int report_feet() throws AltosConfigDataException;
+
+       public abstract void set_report_feet(int radio_10mw);
 }
index 57de0a4137406a38b9ca54f9fedd10d8ecc27b1f..bf94b676c4bf795738effd637d54350f93375223 100644 (file)
@@ -35,6 +35,7 @@ public class AltosEepromRecordMega extends AltosEepromRecord {
                case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+               case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
                        return data32(16);
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
                        return data16(14);
@@ -49,6 +50,7 @@ public class AltosEepromRecordMega extends AltosEepromRecord {
                case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+               case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
                        return data32(20);
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
                        return data16(16);
@@ -63,6 +65,7 @@ public class AltosEepromRecordMega extends AltosEepromRecord {
                case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+               case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
                        return data32(24);
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
                        return data16(18);
@@ -119,6 +122,8 @@ public class AltosEepromRecordMega extends AltosEepromRecord {
                switch (log_format) {
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
                        return AltosLib.model_mpu6000;
+               case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
+                       return AltosLib.model_bmi088;
                }
                return AltosLib.MISSING;
        }
@@ -126,6 +131,7 @@ public class AltosEepromRecordMega extends AltosEepromRecord {
        private boolean sensor_normalized() {
                switch (log_format) {
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+               case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
                        return true;
                }
                return false;
@@ -134,6 +140,7 @@ public class AltosEepromRecordMega extends AltosEepromRecord {
        private int mag_model() {
                switch (log_format) {
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+               case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
                        return AltosLib.model_mmc5983;
                }
                return AltosLib.MISSING;
index 3b19ee682ff9b633884c45fdf33584f5447affa6..2c2089d7ebb90d42a5e583fb2f2ca08d4385fc2a 100644 (file)
@@ -87,6 +87,7 @@ public class AltosEepromRecordSet implements AltosRecordSet {
                case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+               case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
                        record = new AltosEepromRecordMega(eeprom);
                        break;
                case AltosLib.AO_LOG_FORMAT_TELEMETRUM:
index 0ff27c75421618a49753afdd7538af45da7bbcd6..87570a79a5458ec580198c714f56d3c92c39e78a 100644 (file)
@@ -52,6 +52,7 @@ public class AltosIMU implements Cloneable {
        public static final double      counts_per_g_mpu = 2048.0;
        public static final double      counts_per_g_bmx = 2048.0;
        public static final double      counts_per_g_adxl = 20.5;
+       public static final double      counts_per_g_bmi088 = 1365.0;
 
        private static double counts_per_g(int imu_type, int imu_model) {
                switch (imu_model) {
@@ -62,6 +63,8 @@ public class AltosIMU implements Cloneable {
                        return counts_per_g_adxl;
                case AltosLib.model_bmx160:
                        return counts_per_g_bmx;
+               case AltosLib.model_bmi088:
+                       return counts_per_g_bmi088;
                }
 
                switch (imu_type) {
@@ -93,6 +96,7 @@ public class AltosIMU implements Cloneable {
        public static final double      GYRO_FULLSCALE_DEGREES_BMX = 2000.0;
        public static final double      GYRO_COUNTS_BMX = 32767.0;
        public static final double      counts_per_degree_bmx = GYRO_COUNTS_BMX / GYRO_FULLSCALE_DEGREES_BMX;
+       public static final double      counts_per_degree_bmi088 = 16.384;
 
        private static double counts_per_degree(int imu_type, int imu_model) {
                switch (imu_model) {
@@ -101,6 +105,8 @@ public class AltosIMU implements Cloneable {
                        return counts_per_degree_mpu;
                case AltosLib.model_bmx160:
                        return counts_per_degree_bmx;
+               case AltosLib.model_bmi088:
+                       return counts_per_degree_bmi088;
                }
 
                switch (imu_type) {
@@ -185,6 +191,21 @@ public class AltosIMU implements Cloneable {
                        }
                        return true;
                }
+               if (line.startsWith("BMI088:")) {
+                       String[] items = line.split("\\s+");
+
+                       imu_model = AltosLib.model_bmi088;
+
+                       if (items.length >= 7) {
+                               accel_along = Integer.parseInt(items[1]);
+                               accel_across = Integer.parseInt(items[2]);
+                               accel_through = Integer.parseInt(items[3]);
+                               gyro_roll = Integer.parseInt(items[4]);
+                               gyro_pitch = Integer.parseInt(items[5]);
+                               gyro_yaw = Integer.parseInt(items[6]);
+                       }
+                       return true;
+               }
 
                return false;
        }
index b2fd3c6f56833b938cc91392f12fa7c48f87f951..25a36096f6d98b275dcf2810a990b384fd02a19a 100644 (file)
@@ -182,6 +182,12 @@ public class AltosIdleFetch implements AltosDataProvider {
                               AltosIdler.idle_ms5607,
                               AltosIdler.idle_sensor_metrum),
 
+               new AltosIdler("TeleMetrum-v4",
+                              AltosIdler.idle_gps,
+                              AltosIdler.idle_adxl375,
+                              AltosIdler.idle_ms5607,
+                              AltosIdler.idle_sensor_metrum),
+
                new AltosIdler("TeleMega-v0",
                               AltosIdler.idle_gps,
                               AltosIdler.idle_mma655x,
@@ -218,6 +224,12 @@ public class AltosIdleFetch implements AltosDataProvider {
                               AltosIdler.idle_ms5607,
                               AltosIdler.idle_imu, AltosIdler.idle_mag,
                               AltosIdler.idle_sensor_mega),
+               new AltosIdler("TeleMega-v6",
+                              AltosIdler.idle_gps,
+                              AltosIdler.idle_adxl375,
+                              AltosIdler.idle_ms5607,
+                              AltosIdler.idle_imu, AltosIdler.idle_mag,
+                              AltosIdler.idle_sensor_mega),
                new AltosIdler("EasyMega-v1",
                               AltosIdler.idle_mma655x,
                               AltosIdler.idle_ms5607,
index 47c10cbf2fdac279f9a9cfe4b4607c221d691421..5293badce58eee47e6fb109741e7caced1e2be04 100644 (file)
@@ -386,6 +386,7 @@ public class AltosLib {
        public static final int AO_LOG_FORMAT_TELEMEGA_4 = 19;
        public static final int AO_LOG_FORMAT_EASYMOTOR = 20;
        public static final int AO_LOG_FORMAT_TELEMEGA_5 = 21;
+       public static final int AO_LOG_FORMAT_TELEMEGA_6 = 22;
        public static final int AO_LOG_FORMAT_NONE = 127;
 
        public static final int model_mpu6000 = 0;
@@ -394,6 +395,7 @@ public class AltosLib {
        public static final int model_bmx160 = 3;
        public static final int model_hmc5883 = 4;
        public static final int model_mmc5983 = 5;
+       public static final int model_bmi088 = 6;
 
        public static boolean isspace(int c) {
                switch (c) {
@@ -663,6 +665,8 @@ public class AltosLib {
                        return product_easymotor;
                case AO_LOG_FORMAT_TELEMEGA_5:
                        return product_telemega;
+               case AO_LOG_FORMAT_TELEMEGA_6:
+                       return product_telemega;
                case AO_LOG_FORMAT_NONE:
                        return product_altusmetrum;
                default:
index 54dd04dac4c2695f006f4b73528a47d556ac98f4..5fa5fc3bbb9955676dfa96c3391ace005bccd808 100644 (file)
@@ -82,6 +82,7 @@ public abstract class AltosTelemetry implements AltosDataProvider {
        final static int packet_type_mini3 = 0x11;
        final static int packet_type_mega_sensor_bmx160 = 0x12;
        final static int packet_type_mega_norm_mpu6000_mmc5983 = 0x13;
+       final static int packet_type_mega_norm_bmi088_mmc5983 = 0x14;
 
        static AltosTelemetry parse_hex(String hex)  throws ParseException, AltosCRCException {
                AltosTelemetry  telem = null;
index 7f6626dbf286074c4ae6782a8f6032c3c63ef904..bea217c82dcbe10ad00497df323018a226ec5536 100644 (file)
@@ -99,6 +99,9 @@ public abstract class AltosTelemetryStandard extends AltosTelemetry {
                case packet_type_mega_norm_mpu6000_mmc5983:
                        telem = new AltosTelemetryMegaNorm(bytes, AltosLib.model_mpu6000, AltosLib.model_mmc5983);
                        break;
+               case packet_type_mega_norm_bmi088_mmc5983:
+                       telem = new AltosTelemetryMegaNorm(bytes, AltosLib.model_bmi088, AltosLib.model_mmc5983);
+                       break;
                default:
                        telem = new AltosTelemetryRaw(bytes);
                        break;
index e275cce121793f470c89f5a6e7116e87ef31842e..e502fdfff0d0ea29a2de3efd0437268383283726 100644 (file)
@@ -9,6 +9,8 @@ altoslib/
        Declare new USB ids
        Declare new Product name
        Add item to product_name function
+       Add entry in product_id_from_log_format
+       Declare new sensor model
 
  2. AltosIdleFetch.java
 
@@ -50,3 +52,15 @@ altosuilib/
  1. AltosUSBDevice.java
 
        Add new product ID as appropriate to matchProduct
+
+ao-tools/lib/
+
+ 1. ao-eeprom-read.h
+
+       Add new log file format as appropriate
+
+ao-tools/ao-eeprom/
+
+ 1. ao-eeprom.c
+
+       Parse new log file contents
index 75b294d9b2c01d5acdd440d80a64350420580045..88f8b0808a92a59c68aab2cf982d43bac124949d 100644 (file)
@@ -43,6 +43,7 @@ public class AltosConfigFCUI
        JLabel                  radio_frequency_label;
        JLabel                  radio_enable_label;
        JLabel                  radio_10mw_label;
+       JLabel                  report_feet_label;
        JLabel                  rate_label;
        JLabel                  aprs_interval_label;
        JLabel                  aprs_ssid_label;
@@ -71,6 +72,7 @@ public class AltosConfigFCUI
        JLabel                  radio_calibration_value;
        JRadioButton            radio_enable_value;
        JRadioButton            radio_10mw_value;
+       JComboBox<String>       report_feet_value;
        AltosUIRateList         rate_value;
        JComboBox<String>       aprs_interval_value;
        JComboBox<Integer>      aprs_ssid_value;
@@ -183,6 +185,11 @@ public class AltosConfigFCUI
                "10"
        };
 
+       static String[]         report_feet_values = {
+               "Meters",
+               "Feet",
+       };
+
        /* A window listener to catch closing events and tell the config code */
        class ConfigListener extends WindowAdapter {
                AltosConfigFCUI ui;
@@ -358,6 +365,13 @@ public class AltosConfigFCUI
                        radio_10mw_value.setToolTipText("Older firmware could not limit radio power");
        }
 
+       void set_report_feet_tool_tip() {
+               if (report_feet_value.isVisible())
+                       report_feet_value.setToolTipText("Units used after landing to beep max height");
+               else
+                       report_feet_value.setToolTipText("Older firmware always beeps max height in meters");
+       }
+
        /* Build the UI using a grid bag */
        public AltosConfigFCUI(JFrame in_owner, boolean remote) {
                super (in_owner, title, false);
@@ -616,6 +630,32 @@ public class AltosConfigFCUI
                set_radio_10mw_tool_tip();
                row++;
 
+               /* Report feet */
+               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;
+               report_feet_label = new JLabel("Beep max height in:");
+               pane.add(report_feet_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;
+               report_feet_value = new JComboBox<String>(report_feet_values);
+               report_feet_value.setEditable(false);
+               report_feet_value.addItemListener(this);
+               pane.add(report_feet_value, c);
+               set_report_feet_tool_tip();
+               row++;
+
                /* Telemetry Rate */
                c = new GridBagConstraints();
                c.gridx = 0; c.gridy = row;
@@ -1488,6 +1528,31 @@ public class AltosConfigFCUI
                        return AltosLib.MISSING;
        }
 
+       public void set_report_feet(int new_report_feet) {
+               if (new_report_feet != AltosLib.MISSING) {
+                       if (new_report_feet >= report_feet_values.length)
+                               new_report_feet = 0;
+                       if (new_report_feet < 0) {
+                               report_feet_value.setEnabled(false);
+                               new_report_feet = 0;
+                       } else {
+                               report_feet_value.setEnabled(true);
+                       }
+                       report_feet_value.setSelectedIndex(new_report_feet);
+               }
+               report_feet_value.setVisible(new_report_feet != AltosLib.MISSING);
+               report_feet_label.setVisible(new_report_feet != AltosLib.MISSING);
+
+               set_report_feet_tool_tip();
+       }
+
+       public int report_feet() {
+               if (report_feet_value.isVisible())
+                       return report_feet_value.getSelectedIndex();
+               else
+                       return AltosLib.MISSING;
+       }
+
        String[] tracker_motion_values() {
                if (AltosConvert.imperial_units)
                        return tracker_motion_values_ft;
index 41f0d6ad4085de9ecc283b5cdd1cd19f27a9a3e4..e61dd3a82c9947ab5703709758c2eb0820c98c9e 100644 (file)
@@ -124,7 +124,8 @@ FIRMWARE_TD=$(FIRMWARE_TD_3_0)
 
 FIRMWARE_TM_2_0=$(top_srcdir)/src/telemetrum-v2.0/telemetrum-v2.0-$(VERSION).ihx
 FIRMWARE_TM_3_0=$(top_srcdir)/src/telemetrum-v3.0/telemetrum-v3.0-$(VERSION).ihx
-FIRMWARE_TM=$(FIRMWARE_TM_2_0) $(FIRMWARE_TM_3_0)
+#FIRMWARE_TM_4_0=$(top_srcdir)/src/telemetrum-v4.0/telemetrum-v4.0-$(VERSION).ihx
+FIRMWARE_TM=$(FIRMWARE_TM_2_0) $(FIRMWARE_TM_3_0) $(FIRMWARE_TM_4_0)
 
 FIRMWARE_TELEMINI_3_0=$(top_srcdir)/src/telemini-v3.0/telemini-v3.0-$(VERSION).ihx
 FIRMWARE_TELEMINI=$(FIRMWARE_TELEMINI_3_0)
index 7abd4d03f15ce4d2205ae0b1d7dd097fa41fe322..620a5cefea5185a4c7ec9785f4e4dc2570380fe4 100644 (file)
@@ -122,6 +122,7 @@ Section "Firmware"
 
        File "../src/telemetrum-v2.0/telemetrum-v2.0-${VERSION}.ihx"
        File "../src/telemetrum-v3.0/telemetrum-v3.0-${VERSION}.ihx"
+;      File "../src/telemetrum-v4.0/telemetrum-v4.0-${VERSION}.ihx"
        File "../src/telemini-v3.0/telemini-v3.0-${VERSION}.ihx"
        File "../src/telegps-v1.0/telegps-v1.0-${VERSION}.ihx"
        File "../src/telegps-v2.0/telegps-v2.0-${VERSION}.ihx"
diff --git a/ao-bringup/test-easymini-v3.0 b/ao-bringup/test-easymini-v3.0
new file mode 100755 (executable)
index 0000000..779a800
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+VERSION=3.0
+PRODUCT=EasyMini
+BASE=`echo $PRODUCT | tr 'A-Z' 'a-z'`
+
+echo "$PRODUCT-v$VERSION Test Program"
+echo "Copyright 2022 by Bdale Garbee.  Released under GPL v3"
+echo
+echo "Expectations:"
+echo -e "\t$PRODUCT v$VERSION powered from USB"
+echo
+
+found=0
+while [ $found -eq 0 ]; do
+    (ao-list; echo END END END END) | while read product serial dev; do
+       case "$product" in
+           "$PRODUCT-v$VERSION")
+
+               found=1
+               echo -e '\e[34m'Testing $product $serial $dev'\e[39m'
+               echo ""
+               
+               sleep 2
+
+               ./test-igniters-nowait "$dev" drogue main
+               echo ""
+
+               echo "Testing baro sensor"
+               ../ao-tools/ao-test-baro/ao-test-baro --tty="$dev"
+
+               if [ $? -ne 0 ]; then
+                   echo -e '\e[31m'"$PRODUCT-$VERSION serial $serial failed"'\e[39m'
+                   exit 1
+               fi
+               echo""
+
+               FLASHSIZE=1048576
+
+               echo "Testing flash"
+               ../ao-tools/ao-test-flash/ao-test-flash --tty="$dev" "$FLASHSIZE"
+
+               if [ $? -ne 0 ]; then
+                   echo -e '\e[31m'"$PRODUCT-$VERSION serial $serial failed"'\e[39m'
+                   exit 1
+               fi
+               echo ""
+
+               echo -e '\e[32m'"$PRODUCT-v$VERSION" serial "$serial" is ready to ship'\e[39m'
+               exit 0
+               ;;
+           END)
+               exit 2
+               ;;
+       esac
+    done
+    result=$?
+    if [ $result -ne 2 ]; then
+       exit $result
+    fi
+    sleep 0.25
+done
diff --git a/ao-bringup/test-easymotor b/ao-bringup/test-easymotor
deleted file mode 100755 (executable)
index a99ca9d..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/bin/sh
-
-VERSION=2
-PRODUCT=EasyMotor
-BASE=`echo $PRODUCT | tr 'A-Z' 'a-z'`
-
-echo "$PRODUCT-v$VERSION Test Program"
-echo "Copyright 2021 by Bdale Garbee.  Released under GPL v3"
-echo
-echo "Expectations:"
-echo "\t$PRODUCT v$VERSION powered from USB"
-echo
-
-ret=1
-ao-list | while read product serial dev; do
-    case "$product" in
-       "$PRODUCT-v$VERSION")
-
-           echo "Testing $product $serial $dev"
-           echo ""
-
-           FLASHSIZE=8388608
-
-           echo "Testing flash"
-           ../ao-tools/ao-test-flash/ao-test-flash --tty="$dev" "$FLASHSIZE"
-
-           case $? in
-               0)
-                   ;;
-               *)
-                   echo "failed"
-                   exit 1
-           esac
-           echo""
-
-           echo "$PRODUCT-v$VERSION" serial "$serial" is ready to ship
-           ret=0
-           ;;
-    esac
-done
diff --git a/ao-bringup/test-easymotor-v3 b/ao-bringup/test-easymotor-v3
new file mode 100755 (executable)
index 0000000..e1454b8
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+VERSION=3
+PRODUCT=EasyMotor
+BASE=`echo $PRODUCT | tr 'A-Z' 'a-z'`
+
+echo "$PRODUCT-v$VERSION Test Program"
+echo "Copyright 2022 by Bdale Garbee.  Released under GPL v3"
+echo
+echo "Expectations:"
+echo "\t$PRODUCT v$VERSION powered from USB"
+echo "\t\tand precision 2:1 resistor divider feeding pressure input from 5V out"
+echo
+
+ret=1
+ao-list | while read product serial dev; do
+    case "$product" in
+       "$PRODUCT-v$VERSION")
+
+           echo "Testing $product $serial $dev"
+           echo ""
+
+           FLASHSIZE=8388608
+
+           echo "Testing flash"
+           ../ao-tools/ao-test-flash/ao-test-flash --tty="$dev" "$FLASHSIZE"
+
+           echo "" 
+
+           echo "Testing pressure sensor input"
+           ../ao-tools/ao-test-pressure/ao-test-pressure --tty="$dev" 
+
+           case $? in
+               0)
+                   ;;
+               *)
+                   echo "failed"
+                   exit 1
+           esac
+           echo""
+
+           echo "$PRODUCT-v$VERSION" serial "$serial" is ready to ship
+           ret=0
+           ;;
+    esac
+done
index 2041a448f9d8acc303dbdbeb71cdf0dcf3ad3c06..1960c4d679d314529b58a8bcaf6b7524a46409b0 100755 (executable)
@@ -18,13 +18,13 @@ else
        exit 1
 fi
 
-VERSION=1.0
+VERSION=3.0
 PRODUCT=EasyMini
 BASE=`echo $PRODUCT | tr 'A-Z' 'a-z'`
 echo $FILE
 
 echo "$PRODUCT v$VERSION Turn-On and Calibration Program"
-echo "Copyright 2010 by Bdale Garbee.  Released under GPL v2"
+echo "Copyright 2022 by Bdale Garbee.  Released under GPL v3"
 echo
 echo "Expectations:"
 echo "\t$PRODUCT v$VERSION powered from USB"
@@ -49,11 +49,8 @@ esac
 #
 # Use released versions of everything
 #
-FLASH_FILE=~/altusmetrumllc/Binaries/loaders/easymini-v1.0-altos-flash-*.elf
-ALTOS_FILE=~/altusmetrumllc/Binaries/easymini-v1.0-*.elf
-
-#FLASH_FILE=../src/$BASE-v$VERSION/flash-loader/$BASE-v$VERSION-altos-flash-*.elf
-#ALTOS_FILE=../src/$BASE-v$VERSION/*.ihx
+FLASH_FILE=~/altusmetrumllc/Binaries/loaders/easymini-v3.0-altos-flash-*.elf
+ALTOS_FILE=~/altusmetrumllc/Binaries/easymini-v3.0-*.elf
 
 echo $FLASH_LPC $FLASH_FILE
 
@@ -67,6 +64,6 @@ $USBLOAD --serial=$SERIAL $ALTOS_FILE || exit 1
 
 sleep 1
 
-./test-easymini-v1.0
+./test-easymini-v3.0
 
 exit $?
diff --git a/ao-bringup/turnon_easymini_v1.0 b/ao-bringup/turnon_easymini_v1.0
new file mode 100755 (executable)
index 0000000..2041a44
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+if [ -x ../ao-tools/ao-flash/ao-flash-lpc ]; then
+       FLASH_LPC=../ao-tools/ao-flash/ao-flash-lpc
+elif [ -x /usr/bin/ao-flash-lpc ]; then
+       FLASH_LPC=/usr/bin/ao-flash-lpc
+else
+       echo "Can't find ao-flash-lpc!  Aborting."
+       exit 1
+fi
+
+if [ -x ../ao-tools/ao-usbload/ao-usbload ]; then
+       USBLOAD=../ao-tools/ao-usbload/ao-usbload
+elif [ -x /usr/bin/ao-usbload ]; then
+       USBLOAD=/usr/bin/ao-usbload
+else
+       echo "Can't find ao-usbload!  Aborting."
+       exit 1
+fi
+
+VERSION=1.0
+PRODUCT=EasyMini
+BASE=`echo $PRODUCT | tr 'A-Z' 'a-z'`
+echo $FILE
+
+echo "$PRODUCT v$VERSION Turn-On and Calibration Program"
+echo "Copyright 2010 by Bdale Garbee.  Released under GPL v2"
+echo
+echo "Expectations:"
+echo "\t$PRODUCT v$VERSION powered from USB"
+echo "\t\twith ST-Link-V2 cabled to debug header"
+echo
+
+case $# in
+    1)
+       SERIAL="$1"
+       echo "$PRODUCT-$VERSION serial number: $SERIAL" 
+       ;;
+    0)
+       echo -n "$PRODUCT-$VERSION serial number: "
+       read SERIAL
+       ;;
+    *)
+       echo "Usage: $0 <serial-number>" 1>&2
+       exit 1;
+       ;;
+esac
+
+#
+# Use released versions of everything
+#
+FLASH_FILE=~/altusmetrumllc/Binaries/loaders/easymini-v1.0-altos-flash-*.elf
+ALTOS_FILE=~/altusmetrumllc/Binaries/easymini-v1.0-*.elf
+
+#FLASH_FILE=../src/$BASE-v$VERSION/flash-loader/$BASE-v$VERSION-altos-flash-*.elf
+#ALTOS_FILE=../src/$BASE-v$VERSION/*.ihx
+
+echo $FLASH_LPC $FLASH_FILE
+
+$FLASH_LPC $FLASH_FILE || exit 1
+
+sleep 1
+
+echo $USBLOAD $ALTOS_FILE
+
+$USBLOAD --serial=$SERIAL $ALTOS_FILE || exit 1
+
+sleep 1
+
+./test-easymini-v1.0
+
+exit $?
index 306e0ad9c4e7d8b0c9676c0d97a4147374c80b1e..4678430d1498fcdff9b22afd54b64b0c5dfdea02 100755 (executable)
@@ -1,29 +1,35 @@
 #!/bin/sh
 
-PRODUCT=EasyMotor
-VERSION=2
-REPO=~/altusmetrumllc/Binaries
-
-if [ -x /usr/bin/dfu-util ]; then
-    DFU_UTIL=/usr/bin/dfu-util
+if [ -x ../ao-tools/ao-flash/ao-flash-lpc ]; then
+       FLASH_LPC=../ao-tools/ao-flash/ao-flash-lpc
+elif [ -x /usr/bin/ao-flash-lpc ]; then
+       FLASH_LPC=/usr/bin/ao-flash-lpc
 else
-    echo "Can't find dfu-util! Aborting."
-    exit 1
+       echo "Can't find ao-flash-lpc!  Aborting."
+       exit 1
 fi
 
-if [ -x /usr/bin/ao-usbload ]; then
+if [ -x ../ao-tools/ao-usbload/ao-usbload ]; then
+       USBLOAD=../ao-tools/ao-usbload/ao-usbload
+elif [ -x /usr/bin/ao-usbload ]; then
        USBLOAD=/usr/bin/ao-usbload
 else
        echo "Can't find ao-usbload!  Aborting."
        exit 1
 fi
 
+VERSION=3
+PRODUCT=EasyMotor
+BASE=`echo $PRODUCT | tr 'A-Z' 'a-z'`
+echo $FILE
+
 echo "$PRODUCT v$VERSION Turn-On and Calibration Program"
-echo "Copyright 2021 by Bdale Garbee.  Released under GPL v3"
+echo "Copyright 2022 by Bdale Garbee.  Released under GPL v3"
 echo
 echo "Expectations:"
-echo "\t$PRODUCT v$VERSION"
-echo "\t\twith USB cable attached"
+echo "\t$PRODUCT v$VERSION powered from USB"
+echo "\t\twith ST-Link-V2 cabled to debug header"
+echo "\t\tand precision 2:1 resistor divider feeding pressure input from 5V out"
 echo
 
 case $# in
@@ -40,36 +46,49 @@ case $# in
        exit 1;
        ;;
 esac
+otootor
+#
+# Use released versions of everything
+#
+FLASH_FILE=~/altusmetrumllc/Binaries/loaders/easymotor-v3-altos-flash-*.elf
+ALTOS_FILE=~/altusmetrumllc/Binaries/easymotor-v3-*.elf
 
+echo $FLASH_LPC $FLASH_FILE
 
-echo $DFU_UTIL
+$FLASH_LPC $FLASH_FILE || exit 1
 
-$DFU_UTIL -v -v -R -a 0 -s 0x08000000:leave -D $REPO/loaders/easymotor-v$VERSION*.bin
+sleep 1
 
-sleep 3
+echo $USBLOAD $ALTOS_FILE
 
-$USBLOAD --serial=$SERIAL $REPO/easymotor-v$VERSION*.elf || exit 1
+$USBLOAD --serial=$SERIAL $ALTOS_FILE || exit 1
 
-sleep 5
+sleep 2
 
-dev=`ao-list | awk '/'"$PRODUCT"'-v'"$VERSION"'/ { print $3; exit(0); }'`
+dev=`ao-list | awk '/EasyMotor-v'"$VERSION"'/ { print $3; exit(0); }'`
 
 case "$dev" in
 /dev/tty*)
-       echo "$PRODUCT found on $dev"
+       echo "EasyMotor found on $dev"
        ;;
 *)
-       echo 'No '"$PRODUCT"'-v'"$VERSION"' found'
+       echo 'No EasyMotor-v'"$VERSION"' found'
        exit 1
        ;;
 esac
 
+echo 'E 0' > $dev
+
 failed=1
-while [ $failed =  1 ]; do
+while [ $failed = 1 ]; do
     ../ao-tools/ao-cal-accel/ao-cal-accel $dev
     failed=$?
 done
 
-./test-easymotor
+echo 'E 1' > $dev
+
+sleep 1
+
+./test-easymotor-v3
 
 exit $?
index e2425c99efb4d1aa5f6d4ece74aeb445f29dafe1..38ad38cc2617c9f61df2c91d951b409f2fbf7e33 100644 (file)
@@ -3,7 +3,7 @@ SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-list \
        ao-dumpflash ao-edit-telem ao-dump-up ao-elftohex \
        ao-flash ao-usbload ao-test-igniter ao-test-baro \
        ao-test-flash ao-cal-accel ao-test-gps ao-usbtrng \
-       ao-cal-freq ao-makebin
+       ao-cal-freq ao-makebin ao-test-pressure
 if LIBSTLINK
 SUBDIRS += ao-stmload
 endif
index ec5f9c4da932c91785ad7d20d995993bb45efc3e..8650f38d7f35e0a947447c065d2abc6f93ca32b3 100644 (file)
@@ -354,6 +354,8 @@ main (int argc, char **argv)
                        len = 2;
                        break;
                case AO_LOG_FORMAT_TELEMEGA_4:
+               case AO_LOG_FORMAT_TELEMEGA_5:
+               case AO_LOG_FORMAT_TELEMEGA_6:
                        len = 32;
                        max_adc= 4095;
                        adc_ref = 3.3;
@@ -375,6 +377,11 @@ main (int argc, char **argv)
                }
                if (arg_len)
                        len = arg_len;
+               if (len == 0) {
+                       fprintf(stderr, "Unknown eeprom format %d and no specified length\n",
+                               eeprom->log_format);
+                       exit(1);
+               }
                if (verbose)
                        printf("config major %d minor %d log format %d total %u len %d\n",
                               eeprom->config.major,
@@ -421,6 +428,8 @@ main (int argc, char **argv)
                                case AO_LOG_FORMAT_TELEMEGA_3:
                                case AO_LOG_FORMAT_EASYMEGA_2:
                                case AO_LOG_FORMAT_TELEMEGA_4:
+                               case AO_LOG_FORMAT_TELEMEGA_5:
+                               case AO_LOG_FORMAT_TELEMEGA_6:
                                        log_mega = (struct ao_log_mega *) &eeprom->data[pos];
                                        switch (log_mega->type) {
                                        case AO_LOG_FLIGHT:
index ed9284397dc3a817b4dad9139ffdb33a242b5633..c9174d6a8d520ef6be1ca50af6d9e95b010b3641 100644 (file)
@@ -1,3 +1,3 @@
-bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x ao-reset-lpc
+bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x ao-reset-lpc ao-flash-samd21
 
-man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1 ao-reset-lpc.1
+man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1 ao-reset-lpc.1 ao-flash-samd21.1
index 9e1d1cdb8ade7b320e15d08bd951fa2afeee77d8..62e223db82f2cf67f3f26d6b1e45299395538d54 100755 (executable)
@@ -12,5 +12,6 @@ echo "program $file reset" > $cmds
 openocd \
        -f interface/stlink-v2.cfg \
        -f target/lpc11xx.cfg \
+       -c 'adapter speed 1000' \
        -f $cmds \
        -c shutdown
diff --git a/ao-tools/ao-flash/ao-flash-samd21 b/ao-tools/ao-flash/ao-flash-samd21
new file mode 100755 (executable)
index 0000000..f20d526
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+case "$#" in
+1)
+    ;;
+*)
+    echo "usage: $0 <filename> ..."
+    exit 1
+    ;;
+esac
+openocd -f interface/stlink.cfg \
+        -c 'transport select hla_swd' \
+       -c 'set CHIPNAME at91samd21g18' \
+       -c 'set CPUTAPID 0x0bc11477' \
+       -f target/at91samdXX.cfg \
+       -c init \
+       -c 'reset halt' \
+       -c 'at91samd bootloader 0' \
+       -c "flash write_image erase unlock $1" \
+       -c "shutdown"
diff --git a/ao-tools/ao-flash/ao-flash-samd21.1 b/ao-tools/ao-flash/ao-flash-samd21.1
new file mode 100644 (file)
index 0000000..d624fe0
--- /dev/null
@@ -0,0 +1,36 @@
+.\"
+.\" 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-samd21" ""
+.SH NAME
+ao-flash-samd21 \- flash a program to an SAMD21-based AltOS device using openocd
+.SH SYNOPSIS
+.B "ao-flash-samd21"
+\fIfile.elf\fP
+.SH DESCRIPTION
+.I ao-flash-samd21
+loads the specified .elf file into the target device flash memory.
+.SH USAGE
+.I ao-flash-samd21
+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
diff --git a/ao-tools/ao-test-pressure/Makefile.am b/ao-tools/ao-test-pressure/Makefile.am
new file mode 100644 (file)
index 0000000..e4913fd
--- /dev/null
@@ -0,0 +1,11 @@
+bin_PROGRAMS=ao-test-pressure
+
+AM_CFLAGS=$(WARN_CFLAGS) -I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS)
+
+ao_test_pressure_DEPENDENCIES = $(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_test_pressure_LDADD=$(top_builddir)/ao-tools/lib/libao-tools.a $(LIBUSB_LIBS)
+
+ao_test_pressure_SOURCES=ao-test-pressure.c
+
+man_MANS = ao-test-pressure.1
diff --git a/ao-tools/ao-test-pressure/ao-test-pressure.1 b/ao-tools/ao-test-pressure/ao-test-pressure.1
new file mode 100644 (file)
index 0000000..4c957e4
--- /dev/null
@@ -0,0 +1,56 @@
+.\"
+.\" Copyright © 2022 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.
+.\"
+.\"
+.TH AO-LOAD 1 "ao-test-pressure" ""
+.SH NAME
+ao-test-baro \- test AltOS motor testing pressure sensor
+.SH SYNOPSIS
+.B "ao-test-pressure"
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
+\fIpressure-name...\fP
+.SH DESCRIPTION
+.I ao-test-pressure
+makes sure the pressure sensor input is near mid-band when driven by a 2:1
+resistive divider across the sensor power rail.
+.SH OPTIONS
+.TP
+\-T tty-device | --tty tty-device
+This selects which tty device the debugger uses to communicate with
+the target device.
+.TP
+\-D AltOS-device | --device AltOS-device
+Search for a connected device. This requires an argument of one of the
+following forms:
+.IP
+TeleMega:2
+.br
+TeleMega
+.br
+2
+.IP
+Leaving out the product name will cause the tool to select a suitable
+product, leaving out the serial number will cause the tool to match
+one of the available devices.
+.SH USAGE
+.I ao-test-pressure
+opens the target device and queries the current pressure sensor input data
+.SH AUTHOR
+Bdale Garbee
diff --git a/ao-tools/ao-test-pressure/ao-test-pressure.c b/ao-tools/ao-test-pressure/ao-test-pressure.c
new file mode 100644 (file)
index 0000000..c83ce0e
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright © 2022 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.
+ */
+
+#include <err.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdbool.h>
+#include "ao-elf.h"
+#include "ccdbg.h"
+#include "cc-usb.h"
+#include "cc.h"
+#include "ao-verbose.h"
+
+static const struct option options[] = {
+       { .name = "tty", .has_arg = 1, .val = 'T' },
+       { .name = "device", .has_arg = 1, .val = 'D' },
+       { .name = "raw", .has_arg = 0, .val = 'r' },
+       { .name = "verbose", .has_arg = 1, .val = 'v' },
+       { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+       fprintf(stderr, "usage: %s [--verbose=<verbose>] [--device=<device>] [-tty=<tty>] main|drogue\n", program);
+       exit(1);
+}
+
+static void
+done(struct cc_usb *cc, int code)
+{
+/*     cc_usb_printf(cc, "a\n"); */
+       cc_usb_close(cc);
+       exit (code);
+}
+
+static char **
+tok(char *line) {
+       char    **strs = malloc (sizeof (char *)), *str;
+       int     n = 0;
+
+       while ((str = strtok(line, " \t"))) {
+               line = NULL;
+               strs = realloc(strs, (n + 2) * sizeof (char *));
+               strs[n] = strdup(str);
+               n++;
+       }
+       strs[n] = '\0';
+       return strs;
+}
+
+static void
+free_strs(char **strs) {
+       char    *str;
+       int     i;
+
+       for (i = 0; (str = strs[i]) != NULL; i++)
+               free(str);
+       free(strs);
+}
+
+struct pressure {
+       struct pressure *next;
+       char            **strs;
+};
+
+static struct pressure *
+pressure(struct cc_usb *usb)
+{
+       struct pressure *head = NULL, **tail = &head;
+       cc_usb_printf(usb, "a\n");
+       for (;;) {
+               char    line[512];
+               struct pressure *b;
+
+               cc_usb_getline(usb, line, sizeof (line));
+               b = malloc (sizeof (struct pressure));
+               b->strs = tok(line);
+               b->next = NULL;
+               *tail = b;
+               tail = &b->next;
+               if (strstr(line, "tick:"))
+                       break;
+       }
+       return head;
+}
+
+static void
+free_pressure(struct pressure *b) {
+       struct pressure *n;
+
+       while (b) {
+               n = b->next;
+               free_strs(b->strs);
+               free(b);
+               b = n;
+       }
+}
+
+static int
+do_pressure(struct cc_usb *usb) {
+       struct pressure *b = pressure(usb);
+       char **alt = b->strs;
+
+       if (!alt) {
+               printf("no response\n");
+               free_pressure(b);
+               return 0;
+       }
+
+       double pressure = strtod(alt[5], NULL);
+
+       if (pressure < 15000 || pressure > 16000) {
+               printf ("weird pressure %f\n", pressure);
+               free_pressure(b);
+               return 0;
+       }
+
+       printf ("pressure %f\n", pressure);
+
+       return 1;
+}
+
+int
+main (int argc, char **argv)
+{
+       char                    *device = NULL;
+       int                     c;
+       struct cc_usb           *cc = NULL;
+       char                    *tty = NULL;
+       int                     verbose = 0;
+       int                     ret = 0;
+
+       while ((c = getopt_long(argc, argv, "rT:D:c:s:v:", options, NULL)) != -1) {
+               switch (c) {
+               case 'T':
+                       tty = optarg;
+                       break;
+               case 'D':
+                       device = optarg;
+                       break;
+               case 'v':
+                       verbose++;
+                       break;
+               default:
+                       usage(argv[0]);
+                       break;
+               }
+       }
+
+       ao_verbose = verbose;
+
+       if (verbose > 1)
+               ccdbg_add_debug(CC_DEBUG_BITBANG);
+
+       if (!tty)
+               tty = cc_usbdevs_find_by_arg(device, "AltosFlash");
+       if (!tty)
+               tty = cc_usbdevs_find_by_arg(device, "EasyMotor");
+       if (!tty)
+               tty = getenv("ALTOS_TTY");
+       if (!tty)
+               tty="/dev/ttyACM0";
+
+       cc = cc_usb_open(tty);
+
+       if (!cc)
+               exit(1);
+
+       if (!do_pressure(cc))
+               ret = 1;
+       done(cc, ret);
+}
index 11e4f91d92e0fe6f9daa13b1ff9828ac55de91a9..01c2ef35aa6af46804ad43393d049c2f273f90b6 100644 (file)
@@ -46,6 +46,8 @@
 #define AO_LOG_FORMAT_MICROPEAK2       18      /* 2-byte baro values with header */
 #define AO_LOG_FORMAT_TELEMEGA_4       19      /* 32 byte typed telemega records with 32 bit gyro cal and Bmx160 */
 #define AO_LOG_FORMAT_EASYMOTOR                20      /* 16 byte typed easymotor records with pressure sensor and adxl375 */
+#define AO_LOG_FORMAT_TELEMEGA_5       21      /* 32 byte typed telemega records with 32 bit gyro cal, mpu6000 and mmc5983 */
+#define AO_LOG_FORMAT_TELEMEGA_6       22      /* 32 byte typed telemega records with 32 bit gyro cal, bmi088 and mmc5983 */
 #define AO_LOG_FORMAT_NONE             127     /* No log at all */
 
 enum ao_pyro_flag {
index 73f3d7bed61dde5534fe03631ff62e4b563f49c9..c275baafb2c296b41c011f7872c3c1d4d34330f0 100644 (file)
@@ -565,7 +565,7 @@ ao_hex_file_create(struct ao_hex_image *image, struct ao_sym *symbols, int num_s
 
        /* Stick an EOF after the data
         */
-       record = calloc(sizeof (struct ao_hex_record), 1);
+       record = calloc(1,sizeof (struct ao_hex_record) + 2);
        record->type = AO_HEX_RECORD_EOF;
        record->address = 0;
        record->length = 0;
index 1e023c7ebe8f7b37e99eeef7b044d294a5f531dd..65a4cbad41e54435510828f2e7c3a4ea0eccf81a 100644 (file)
@@ -460,7 +460,7 @@ cc_usb_open(char *tty)
        cfsetospeed(&termios, B9600);
        cfsetispeed(&termios, B9600);
        tcsetattr(cc->fd, TCSAFLUSH, &termios);
-       cc_usb_printf(cc, "\nE 0\nm 0\n");
+       cc_usb_printf(cc, "\nE 0\n");
        do {
                cc->in_count = cc->in_pos = 0;
                _cc_usb_sync(cc, 100, cc_default_timeout);
index 19490ec6e1e351de73f3e7232d8ff70f8b29af49..b41b5398252fdadb09dde4281172060ddabb4818 100644 (file)
@@ -18,13 +18,13 @@ dnl
 dnl Process this file with autoconf to create configure.
 
 AC_PREREQ(2.57)
-AC_INIT([altos], 1.9.12)
-ANDROID_VERSION=35
+AC_INIT([altos], 1.9.13)
+ANDROID_VERSION=36
 AC_CONFIG_SRCDIR([src/kernel/ao.h])
 AM_INIT_AUTOMAKE([foreign dist-bzip2])
 AM_MAINTAINER_MODE
 
-RELEASE_DATE=2022-10-28
+RELEASE_DATE=2023-01-19
 AC_SUBST(RELEASE_DATE)
 
 DOC_DATE=`LC_ALL=C date -d $RELEASE_DATE +'%d %b %Y'`
@@ -570,6 +570,7 @@ ao-tools/ao-flash/Makefile
 ao-tools/ao-test-igniter/Makefile
 ao-tools/ao-test-baro/Makefile
 ao-tools/ao-test-flash/Makefile
+ao-tools/ao-test-pressure/Makefile
 ao-tools/ao-cal-accel/Makefile
 ao-tools/ao-cal-freq/Makefile
 ao-tools/ao-test-gps/Makefile
index 3827f79d260ccbeb2653c2ff915a4ed8ddea351d..d9e5f18867919a7866a895397d273422996d606b 100644 (file)
@@ -17,6 +17,7 @@ FAKETIME=TZ=UTC faketime -f '$(RELEASE_DATE) 00:00:00 i0'
 endif
 
 RELNOTES_INC=\
+       release-notes-1.9.13.inc \
        release-notes-1.9.12.inc \
        release-notes-1.9.11.inc \
        release-notes-1.9.10.inc \
index 9d12f12b324e2d0b02fe86457976dddc49b9f790..8a042df852428aabe98b31bebc987979f7349368 100644 (file)
@@ -5,8 +5,8 @@ Keith Packard <keithp@keithp.com>
 :stylesheet: am.css
 :linkcss:
 :numbered:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
 :pdf-fontsdir: fonts
 
        include::header.adoc[]
index c4f6f96bdf5368588330d7f0d34d448b8ba78cda..14e5634bb95946ba141ade05d9f88613ea862677 100644 (file)
@@ -1,10 +1,13 @@
+extends: default
 base:
   font_family: Open Sans Light
+  font-size: 12
 heading:
   font_color: #78079a
   font_size: 17
 font:
   catalog:
+    merge: true
     Open Sans Light:
       normal: OpenSans-Light.ttf
       italic: OpenSans-LightItalic.ttf
@@ -42,7 +45,7 @@ admonition_label:
 page:
   background_color: ffffff
   layout: portrait
-  margin: [0.5in, 0.67in, 0.75in, 0.67in]
+  margin: [0.5in, 0.67in, 0.67in, 0.67in]
   size: letter
 footer:
   height: 0.5in
@@ -51,13 +54,13 @@ footer:
     left:
       content: '{page-number}'
     right:
-      content: '© 2022 Bdale Garbee and Keith Packard. Creative Commons ShareAlike 3.0 License'
+      content: '© 2023 Bdale Garbee and Keith Packard. Creative Commons ShareAlike 3.0 License'
   verso:
     left:
       content: $footer_recto_right_content
     right:
       content: '{page-number}'
-literal:
+codespan:
   font_family: DejaVu Sans Mono
 code:
   font_family: DejaVu Sans Mono
index b655c0b23a166e0ff480f2b5ec7cc4d68fe1584f..cb9ef4e2f5f59aab6d248147e27f9b1bb32ef5c4 100644 (file)
@@ -5,7 +5,7 @@ Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>; Bob Finch; Anth
 :revdate: 1 Jan 1970
 :icons:
 :icontype: svg
-:copyright: Bdale Garbee and Keith Packard 2022
+:copyright: Bdale Garbee and Keith Packard 2023
 :doctype: book
 :numbered:
 :stylesheet: am.css
@@ -23,8 +23,8 @@ Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>; Bob Finch; Anth
 :easytimer: 1
 :easymotor: 1
 :application: AltosUI
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
 :pdf-fontsdir: fonts
 
        include::header.adoc[]
index 256ffad77cae3c4a11dd142030a67f17ae4bb9f9..c23aab1872b7cf4a874fbfe0fc635b2a2be359dd 100644 (file)
@@ -4,8 +4,8 @@ Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>
 :copyright: Bdale Garbee and Keith Packard 2018
 :stylesheet: am.css
 :linkcss:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
 :pdf-fontsdir: fonts
 :toc:
 
index e85db6811a0be4f467609e846bb632b6e6b8ef39..1d47abdb05e232d82a51b474c9c72a98f6a1b0db 100644 (file)
@@ -1,5 +1,9 @@
 [appendix]
 == Release Notes
+       :leveloffset: 2
+       include::release-notes-1.9.13.adoc[]
+
+       <<<<
        :leveloffset: 2
        include::release-notes-1.9.12.adoc[]
 
index 6a4b1e4abedc9e17b442e34866d1182329ed3afc..322a5ef43ddf983b6175565f9376cb7dddab675d 100644 (file)
@@ -12,8 +12,8 @@ Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>
 :altusmetrum: 1
 :easymini: 1
 :application: AltosUI
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
 :pdf-fontsdir: fonts
 
        include::header.adoc[]
index a33b4b9d2524b8d5a607bda1079ade4a1fade152..3cacba86e292d6387a2f8972014fe1b1f3244721 100644 (file)
@@ -7,7 +7,7 @@ endif::[]
 [license]
 == License
 
-Copyright © 2022 Bdale Garbee and Keith Packard
+Copyright © 2023 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]
 
index f83281769a949ff0dd7884308527b18f40bbfca0..b1c5c10a004f8d6d04198df1a29e00a4fcca4988 100644 (file)
@@ -5,8 +5,8 @@ Keith Packard <keithp@keithp.com>
 :doctype: article
 :stylesheet: am-notoc.css
 :linkcss:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
 :pdf-fontsdir: fonts
 
 == The Google Maps Problem
index c02c613a8287c14a145254545c75d674044c1fdd..fb75aacb4d3dd6d165073059207f05b1bff68060 100644 (file)
@@ -8,8 +8,8 @@ Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>
 :toc:
 :doctype: book
 :numbered:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
 :pdf-fontsdir: fonts
 
        include::header.adoc[]
index 1edee68d4c57293bb32a12e53d854b623a9863c2..3914bc709b1ffbc0d136211601454f9b54547390 100644 (file)
@@ -1,10 +1,27 @@
 == Installation
 
-       EasyMotor needs to be rigidly attached in the airframe, and the
-       long axis of the circuit board needs to be aligned with the axis
-       of flight. By default, the round beeper on the board should be
-       “up” towards the nose cone, and the screw terminal strips should
-       be “down” towards the fins and motor nozzle end of the rocket.
+       [WARNING]
+       Firmware versions prior to 1.9.13 used acceleration to detect
+       launch.  Starting with 1.9.13, the firmware switched to using
+       pressure to trigger recording. Altus Metrum strongly recommends 
+       upgrading all EasyMotor boards to current firmware, the
+       behavior of which is described here.
+
+       EasyMotor needs to be rigidly attached in the airframe, in any
+       convenient orientation.
+
+       === Trigger to Start Recording
+
+       Starting with firmware version 1.9.13, a pressure sensor must
+       be attached, and pressure change is used to initiate and conclude
+       data recording.  Pressure and 3 axes of acceleration are recorded
+       for later analysis.
+
+       EasyMotor looks for a 50 psi rise in pressure from ambient to
+       start recording.  Recording stops when the pressure drops 
+       below 50 psi and shows variation of less than 30 psi for at 
+       least 10 seconds. These conditions are intended to capture all
+       actual burn data including anomalies such as "chuffs".
 
        === Power Switch and Battery
 
index 97926c0e34d6db201dbf5bded2bb56c5eeda90c5..c3d25c96afbf94dbe23e3393df2b5116f6e0cd0f 100644 (file)
@@ -13,8 +13,8 @@ Bdale Garbee <bdale@gag.com>
 :toc:
 :motortest: 1
 :application: Motor Testing
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
 :pdf-fontsdir: fonts
 
        include::header.adoc[]
index 0dbdfc2b5656648c7225334cb1dc23083433ca09..2c04a84d7ce1da424d7a38804244aca1599bb800 100644 (file)
@@ -2,6 +2,6 @@
 :doctype: article
 :stylesheet: am-notoc.css
 :linkcss:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
 :pdf-fontsdir: fonts
diff --git a/doc/release-notes-1.9.13.inc b/doc/release-notes-1.9.13.inc
new file mode 100644 (file)
index 0000000..18c4d91
--- /dev/null
@@ -0,0 +1,26 @@
+= Release Notes for Version 1.9.13
+include::release-head.adoc[]
+:doctype: article
+
+       Version 1.9.13
+
+       == AltOS
+
+       * Add option to beep max height in feet after landing
+
+       * Fix APRS reports to be sent at the correct time and spacing.
+
+       * Fix possible barometric sensor communication failure when
+         the CPU is busy talking to the radio at the same time. This
+         would cause loss of telemetry and failure to track the state
+         of the rocket during flight. This was aggrevated by the APRS
+         reports getting sent more often than they should.
+
+       * Change EasyMotor v3 code to base logging on motor pressure
+          rather than the accelerometer. This allows use of EasyMotor
+          v3 in a static test stand.
+
+       == AltosUI
+
+       * Add support for configuring the units used to report height
+          after landing on the beeper.
index 33230a3baa8652c9e8d3ba7b96461140a8c92258..b1e3d8adff30b17daf38641bf22e3fcb77ba9f47 100644 (file)
@@ -1,5 +1,9 @@
 [appendix]
 == Release Notes
+       :leveloffset: 2
+       include::release-notes-1.9.13.adoc[]
+
+       <<<<
        :leveloffset: 2
        include::release-notes-1.9.12.adoc[]
 
index 0166396c68c8ac0346a7d15ce1c2d5caaadc53b4..94bead62617f5c91d0dc5bfc8383f63557d152ab 100644 (file)
@@ -1,5 +1,9 @@
 [appendix]
 == Release Notes
+       :leveloffset: 2
+       include::release-notes-1.9.13.adoc[]
+
+       <<<<
        :leveloffset: 2
        include::release-notes-1.9.12.adoc[]
 
index 608a6db363c110737cce7b143c4069789cb17ec5..aae74424966a6867d65c40475c90f6e274e1f488 100644 (file)
@@ -13,8 +13,8 @@ Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>
 :radio: 1
 :gps: 1
 :application: TeleGPS
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
 :pdf-fontsdir: fonts
 
        include::header.adoc[]
index db71d91869bc6c1360355663b8d3b657b5709944..edc6bfc14a3566569509bc88e7eec38cbfeb4cfa 100644 (file)
@@ -6,6 +6,12 @@
        unit, which early customers (before this manual was even written)
        reported were sufficient to successfully use the system.
 
+       [WARNING]
+       The siren used on TeleFire boxes is intended to be heard at a distance
+       outdoors.  We strongly recommend that you do NOT test the system on
+       your kitchen counter when your significant other has a cup of hot
+       coffee in hand nearby!
+
        The remainder of this section assumes the system has already been
        properly configured with callsign, AES key, and bank numbers for each
        TeleFire box.
index 7d48014e7fdc59f81c9c3d1679fa52710f5dd9b3..7ef04a704c8bd7a99af08c9cc31e5b7fff5b096a 100644 (file)
@@ -15,7 +15,9 @@
 
        TeleFire uses an internal sealed lead-acid 12V battery, which is
        charged by an external charger attached through the PowerPole
-       connector on the rear panel.
+       connector on the rear panel.  These connectors were chosen because
+       they are an link:https://www.qsl.net/w2vtm/powerpole.html[ARES/RACES]
+       standard for 12 volt DC power distribution.
 
        Pyro initiation uses the 12V sealed lead-acid battery.  Current
        to any pad can exceed 30A, and with typical igniters every pad on
index 89f98dd451eca44d437c311e25ae45108da4f3c1..b72ec06f2df7d2b06e9b63b6895534006814a91b 100644 (file)
@@ -13,8 +13,8 @@ Bdale Garbee <bdale@gag.com>
 :toc:
 :telelaunch: 1
 :application: TeleLaunch
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
 :pdf-fontsdir: fonts
 
        include::header.adoc[]
index 0df96abdd2b199efd72bd4937f460f7fff728ccb..c867c6cdac0e5eb0dc65c11c4ef51b78937cc8a5 100644 (file)
@@ -8,8 +8,8 @@ Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>
 :doctype: article
 :toc:
 :numbered:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
 :pdf-fontsdir: fonts
 
        include::header.adoc[]
index 8c49346fdf3a7e45f19934c2bea1355f6b0a7bc0..f7fc90826ac9b0288f3a21abe8ccb8e60507284c 100644 (file)
@@ -42,6 +42,7 @@ struct altos_device {
        int                             serial;
        char                            name[256];
        char                            path[256];
+       int                             (*method_1)(int x, int y);
        //%mutable;
 };
 
index d06468f5a379985f4e3dde7f50a8fd035536a807..ac45fbaf3c591b2520d0cb973a2f4fc0e6064547 100644 (file)
@@ -2,4 +2,8 @@
 %{
 #include "libaltos.h"
 %}
-
+%extend altos_device {
+       int method_1(int x, int y) {
+               return ($self->method_1)(x, y);
+       }
+}
index 45adc166059d717fd00b68f9c224b1c4b3dd52af..ff7f1bd9bd3ebdb3b1ba1f1990d2f317f5434fef 100644 (file)
@@ -27,6 +27,7 @@ ARMM3DIRS=\
        telemega-v3.0 telemega-v3.0/flash-loader \
        telemega-v4.0 telemega-v4.0/flash-loader \
        telemega-v5.0 telemega-v5.0/flash-loader \
+       telemega-v6.0 telemega-v6.0/flash-loader \
        telemetrum-v2.0 telemetrum-v2.0/flash-loader \
        telemetrum-v3.0 telemetrum-v3.0/flash-loader \
        telegps-v0.3 telegps-v0.3/flash-loader \
index d9b719973da61e5c52fabe871d8b3111318b7630..c2d5247cd1ea1cfcbf4129b2e7c291e2f87c3ed7 100644 (file)
@@ -23,6 +23,7 @@ OPT=-Os -Wl,-Map=$(PROGNAME)-$(VERSION).map
 
 PICOLIBC_CFLAGS= \
        -specs=picolibc.specs \
+       -Wl,--gc-sections \
        $(PICOLIBC_PRINTF_CFLAGS)
 
 AO_CFLAGS=\
index 1f422a37645bd92d7d7c2764a29c5f3b26a7efde..e3374ac59ee4b501665675f0c025d9dcb10a758e 100644 (file)
@@ -65,6 +65,8 @@ ao_spi_recv_bus(void *block, uint16_t len);
 #define ao_spi_send(block, len, bus) ao_spi_send_bus(block, len)
 #define ao_spi_recv(block, len, bus) ao_spi_recv_bus(block, len)
 
+#define AO_SPI_DUPLEX  0
+
 void
 ao_spi_init(void);
 
index 3ac4cd535b231a8ac11622e5719dfe115839a202..b0645465c586ddb6e88f11f29ff37962cfc61277 100644 (file)
@@ -310,6 +310,7 @@ static uint8_t tncBitStuff;
 /// Buffer to hold the message portion of the AX.25 packet as we prepare it.
 static uint8_t tncBuffer[TNC_BUFFER_SIZE];
 
+#pragma GCC diagnostic ignored "-Wformat-overflow="
 /**
  *   Initialize the TNC internal variables.
  */
diff --git a/src/drivers/ao_bmi088.c b/src/drivers/ao_bmi088.c
new file mode 100644 (file)
index 0000000..d03fcea
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_bmi088.h>
+#include <ao_data.h>
+
+#define AO_BMI088_SPI_SPEED    ao_spi_speed(100000)
+
+#define ao_bmi088_spi_get()    ao_spi_get(AO_BMI088_SPI_BUS, AO_BMI088_SPI_SPEED)
+#define ao_bmi088_spi_put()    ao_spi_put(AO_BMI088_SPI_BUS)
+
+#define ao_bmi088_acc_start()  ao_spi_set_cs(AO_BMI088_ACC_CS_PORT,    \
+                                             (1 << AO_BMI088_ACC_CS_PIN))
+#define ao_bmi088_acc_end()    ao_spi_clr_cs(AO_BMI088_ACC_CS_PORT,    \
+                                             (1 << AO_BMI088_ACC_CS_PIN))
+#define ao_bmi088_gyr_start()  ao_spi_set_cs(AO_BMI088_GYR_CS_PORT,    \
+                                             (1 << AO_BMI088_GYR_CS_PIN))
+#define ao_bmi088_gyr_end()    ao_spi_clr_cs(AO_BMI088_GYR_CS_PORT,    \
+                                             (1 << AO_BMI088_GYR_CS_PIN))
+
+static uint8_t
+_ao_bmi088_acc_reg_read(uint8_t addr)
+{
+       uint8_t v[2];
+       addr |= 0x80;
+       ao_bmi088_acc_start();
+       ao_spi_send(&addr, 1, AO_BMI088_SPI_BUS);
+       ao_spi_recv(v, 2, AO_BMI088_SPI_BUS);   /* part sends garbage for first byte */
+       ao_bmi088_acc_end();
+       return v[1];
+}
+
+static void
+_ao_bmi088_acc_reg_write(uint8_t addr, uint8_t value)
+{
+       uint8_t d[2] = { addr, value };
+       ao_bmi088_acc_start();
+       ao_spi_send(d, 2, AO_BMI088_SPI_BUS);
+       ao_bmi088_acc_end();
+}
+
+static void
+_ao_bmi088_acc_sample_read(struct ao_bmi088_acc_sample *sample)
+{
+       uint8_t dummy;
+       uint8_t addr = BMI088_ACC_DATA | 0x80;
+
+       ao_bmi088_acc_start();
+       ao_spi_send(&addr, 1, AO_BMI088_SPI_BUS);
+       ao_spi_recv(&dummy, 1, AO_BMI088_SPI_BUS);      /* part sends garbage for first byte */
+       ao_spi_recv(sample, sizeof(struct ao_bmi088_acc_sample), AO_BMI088_SPI_BUS);
+       ao_bmi088_acc_end();
+}
+
+static void
+_ao_bmi088_gyr_read(uint8_t addr, void *data, uint8_t len)
+{
+       addr |= 0x80;
+       ao_bmi088_gyr_start();
+       ao_spi_send(&addr, 1, AO_BMI088_SPI_BUS);
+       ao_spi_recv(data, len, AO_BMI088_SPI_BUS);
+       ao_bmi088_gyr_end();
+}
+
+static uint8_t
+_ao_bmi088_gyr_reg_read(uint8_t addr)
+{
+       uint8_t v;
+       _ao_bmi088_gyr_read(addr, &v, 1);
+       return v;
+}
+
+static void
+_ao_bmi088_gyr_reg_write(uint8_t addr, uint8_t value)
+{
+       uint8_t d[2] = { addr, value };
+       ao_bmi088_gyr_start();
+       ao_spi_send(d, 2, AO_BMI088_SPI_BUS);
+       ao_bmi088_gyr_end();
+}
+
+static void
+_ao_bmi088_gyr_sample_read(struct ao_bmi088_gyr_sample *sample)
+{
+       _ao_bmi088_gyr_read(BMI088_GYRO_DATA, sample, sizeof (struct ao_bmi088_gyr_sample));
+}
+
+static void
+ao_bmi088_reset(void)
+{
+       ao_bmi088_spi_get();
+
+       /* reset the two devices */
+       _ao_bmi088_acc_reg_write(BMI088_ACC_SOFTRESET, BMI088_ACC_SOFTRESET_RESET);
+       _ao_bmi088_gyr_reg_write(BMI088_GYRO_SOFTRESET, BMI088_GYRO_SOFTRESET_RESET);
+
+       /* wait 30ms (that's how long the gyro takes */
+       ao_delay(AO_MS_TO_TICKS(30));
+
+       /* force acc part to SPI mode */
+       ao_bmi088_acc_start();
+
+       ao_delay(AO_MS_TO_TICKS(1));
+
+       ao_bmi088_acc_end();
+
+       ao_bmi088_spi_put();
+}
+
+static bool
+ao_bmi088_accel_good(int16_t pos, int16_t neg, int32_t good)
+{
+       int32_t diff = (int32_t) pos - (int32_t) neg;
+
+       return diff >= good;
+}
+
+static void
+ao_bmi088_setup(void)
+{
+       bool working = false;
+       struct ao_bmi088_acc_sample     acc_pos, acc_neg;
+
+       ao_bmi088_reset();
+
+       ao_bmi088_spi_get();
+
+       /* Make sure the two devices are alive */
+       if (_ao_bmi088_acc_reg_read(BMI088_ACC_CHIP_ID) != BMI088_ACC_CHIP_ID_BMI088)
+               goto failure;
+
+       if (_ao_bmi088_gyr_reg_read(BMI088_GYRO_CHIP_ID) != BMI088_GYRO_CHIP_ID_BMI088)
+               goto failure;
+
+       /* Turn on the accelerometer */
+
+       _ao_bmi088_acc_reg_write(BMI088_ACC_PWR_CTRL, BMI088_ACC_PWR_CTRL_ON);
+
+       /* Wait 5ms after changing accel power mode */
+       ao_delay(AO_MS_TO_TICKS(5));
+
+       /* Accel self test. Procedure:
+        *
+        * 1) Set ±24g range by writing 0x03 to register ACC_RANGE (0x41)
+         * 2) Set ODR=1.6kHz, continuous sampling mode, “normal mode” (norm_avg4) by writing 0xA7 to
+         * register ACC_CONF (0x40)
+         * • Continuous filter function: set bit7 in ACC_CONF
+         * • “normal avg4 mode”: ACC_CONF |= 0x02<<4
+         * • ODR=1.6kHz: ACC_CONF |= 0x0C
+         * 3) Wait for > 2 ms
+         * 4) Enable the positive self-test polarity (i.e. write 0x0D to register ACC_SELF_TEST (0x6D))
+         * 5) Wait for > 50ms
+         * 6) Read the accelerometer offset values for each axis (positive self-test response)
+         * 7) Enable the negative self-test polarity (i.e. write 0x09 to register ACC_SELF_TEST (0x6D))
+         * 8) Wait for > 50ms
+         * 9) Read the accelerometer offset values for each axis (negative self-test response)
+         * 10) Disable the self-test (i.e. write 0x00 to register ACC_SELF_TEST (0x6D))
+         * 11) Calculate difference of positive and negative self-test response and compare with the expected
+         * values (see table below)
+         * 12) Wait for > 50ms to let the sensor settle to normal mode steady state operation
+        */
+
+       _ao_bmi088_acc_reg_write(BMI088_ACC_RANGE, BMI088_ACC_RANGE_24);
+
+       _ao_bmi088_acc_reg_write(BMI088_ACC_CONF,
+                                (BMI088_ACC_CONF_BWP_NORMAL << BMI088_ACC_CONF_BWP) |
+                                (BMI088_ACC_CONF_ODR_1600 << BMI088_ACC_CONF_ODR));
+
+       ao_delay(AO_MS_TO_TICKS(2));
+
+       _ao_bmi088_acc_reg_write(BMI088_ACC_SELF_TEST, BMI088_ACC_SELF_TEST_POSITIVE);
+
+       ao_delay(AO_MS_TO_TICKS(50));
+
+       _ao_bmi088_acc_sample_read(&acc_pos);
+
+       _ao_bmi088_acc_reg_write(BMI088_ACC_SELF_TEST, BMI088_ACC_SELF_TEST_NEGATIVE);
+
+       ao_delay(AO_MS_TO_TICKS(50));
+
+       _ao_bmi088_acc_sample_read(&acc_neg);
+
+       _ao_bmi088_acc_reg_write(BMI088_ACC_SELF_TEST, BMI088_ACC_SELF_TEST_OFF);
+
+       /* Self test X and Y must show at least 1000 mg difference,
+        * Z must show at least 500mg difference
+        */
+       if (!ao_bmi088_accel_good(acc_pos.x, acc_neg.x, (int32_t) ao_bmi_accel_to_sample(GRAVITY)))
+               goto failure;
+
+       if (!ao_bmi088_accel_good(acc_pos.y, acc_neg.y, (int32_t) ao_bmi_accel_to_sample(GRAVITY)))
+               goto failure;
+
+       if (!ao_bmi088_accel_good(acc_pos.z, acc_neg.z, (int32_t) ao_bmi_accel_to_sample(GRAVITY/2)))
+               goto failure;
+
+       /*
+        * Self-test gyro
+        *
+        * To trigger the self-test, bit #0 (‘bite_trig’) in address
+        * GYRO_SELF_TEST must be set. When the test is finished, bit
+        * #1 (‘bist_rdy’) will be set by the gyro and the test result
+        * can then be found in bit #2 (‘bist_fail’).  A ‘0’ indicates
+        * that the test was passed without issues. If a failure
+        * occurred, the bit ‘bist_fail’ will be set to ‘1’.
+        */
+
+       _ao_bmi088_gyr_reg_write(BMI088_GYRO_SELF_TEST,
+                                (1 << BMI088_GYRO_SELF_TEST_TRIG_BIST));
+
+       uint8_t gyro_self_test;
+       for(;;) {
+               gyro_self_test = _ao_bmi088_gyr_reg_read(BMI088_GYRO_SELF_TEST);
+               if ((gyro_self_test & (1 << BMI088_GYRO_SELF_TEST_BIST_RDY)) != 0)
+                       break;
+       }
+       if ((gyro_self_test & (1 << BMI088_GYRO_SELF_TEST_BIST_FAIL)) != 0)
+               goto failure;
+
+
+       /* Put accel in desired mode */
+
+       /* 200 Hz sampling rate, normal filter */
+       _ao_bmi088_acc_reg_write(BMI088_ACC_CONF,
+                                (BMI088_ACC_CONF_BWP_NORMAL << BMI088_ACC_CONF_BWP) |
+                                (BMI088_ACC_CONF_ODR_200 << BMI088_ACC_CONF_ODR));
+
+
+       /* 24 g range */
+       _ao_bmi088_acc_reg_write(BMI088_ACC_RANGE, BMI088_ACC_RANGE_24);
+
+       /* Put gyro in desired mode */
+
+       /* 2000°/s range */
+       _ao_bmi088_gyr_reg_write(BMI088_GYRO_RANGE, BMI088_GYRO_RANGE_2000);
+
+       /* 200 Hz sampling rate, 64Hz filter */
+       _ao_bmi088_gyr_reg_write(BMI088_GYRO_BANDWIDTH, BMI088_GYRO_BANDWIDTH_200_64);
+
+       working = true;
+failure:
+       if (!working)
+               AO_SENSOR_ERROR(AO_DATA_BMI088);
+
+       ao_bmi088_spi_put();
+}
+
+struct ao_bmi088_sample        ao_bmi088_current;
+
+static void
+ao_bmi088(void)
+{
+       struct ao_bmi088_sample sample;
+
+       ao_bmi088_setup();
+       for (;;)
+       {
+               ao_bmi088_spi_get();
+               _ao_bmi088_acc_sample_read(&sample.acc);
+               _ao_bmi088_gyr_sample_read(&sample.gyr);
+               ao_bmi088_spi_put();
+               ao_arch_block_interrupts();
+               ao_bmi088_current = sample;
+               AO_DATA_PRESENT(AO_DATA_BMI088);
+               AO_DATA_WAIT();
+               ao_arch_release_interrupts();
+       }
+}
+
+static struct ao_task ao_bmi088_task;
+
+static void
+ao_bmi088_show(void)
+{
+#ifdef AO_LOG_NORMALIZED
+       printf ("BMI088: %7d %7d %7d %7d %7d %7d\n",
+               ao_bmi088_along(&ao_bmi088_current),
+               ao_bmi088_across(&ao_bmi088_current),
+               ao_bmi088_through(&ao_bmi088_current),
+               ao_bmi088_roll(&ao_bmi088_current),
+               ao_bmi088_pitch(&ao_bmi088_current),
+               ao_bmi088_yaw(&ao_bmi088_current));
+#else
+       printf ("Accel: %7d %7d %7d Gyro: %7d %7d %7d\n",
+               ao_bmi088_current.acc.x,
+               ao_bmi088_current.acc.y,
+               ao_bmi088_current.acc.z,
+               ao_bmi088_current.gyr.x,
+               ao_bmi088_current.gyr.y,
+               ao_bmi088_current.gyr.z);
+#endif
+}
+
+static const struct ao_cmds bmi_cmds[] = {
+       { ao_bmi088_show,               "I\0Show BMI088 status" },
+       { 0, 0 }
+};
+
+void
+ao_bmi088_init(void)
+{
+       AO_TICK_TYPE    then;
+       ao_spi_init_cs(AO_BMI088_ACC_CS_PORT, (1 << AO_BMI088_ACC_CS_PIN));
+       ao_spi_init_cs(AO_BMI088_GYR_CS_PORT, (1 << AO_BMI088_GYR_CS_PIN));
+
+       /* force acc part to SPI mode */
+       ao_bmi088_acc_start();
+       then = ao_time();
+       while ((ao_time() - then) < 2)
+               ;
+       ao_bmi088_acc_end();
+
+       ao_add_task(&ao_bmi088_task, ao_bmi088, "bmi088");
+
+       ao_cmd_register(bmi_cmds);
+}
diff --git a/src/drivers/ao_bmi088.h b/src/drivers/ao_bmi088.h
new file mode 100644 (file)
index 0000000..592ed85
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_BMI088_H_
+#define _AO_BMI088_H_
+
+#include <math.h>
+
+struct ao_bmi088_acc_sample {
+       int16_t         x;
+       int16_t         y;
+       int16_t         z;
+};
+
+struct ao_bmi088_gyr_sample {
+       int16_t         x;
+       int16_t         y;
+       int16_t         z;
+};
+
+struct ao_bmi088_sample {
+       struct ao_bmi088_acc_sample acc;
+       struct ao_bmi088_gyr_sample gyr;
+};
+
+extern struct ao_bmi088_sample ao_bmi088_current;
+
+void
+ao_bmi088_init(void);
+
+#define BMI088_ACC_CHIP_ID     0x00
+#define  BMI088_ACC_CHIP_ID_BMI088     0x1e
+
+#define BMI088_ACC_ERR_REG     0x02
+# define BMI088_ACC_ERR_REG_ERROR_CODE 2
+# define BMI088_ACC_ERR_REG_FATAL_ERR  0
+
+#define BMI088_ACC_STATUS      0x03
+# define BMI088_ACC_STATUS_ACC_DRDY    7
+
+#define BMI088_ACC_DATA                0x12
+#define BMI088_ACC_TIME                0x18
+
+#define BMI088_ACC_INT_STAT_1  0x1d
+# define BMI088_ACC_INT_STAT_1_ACC_DRDY        7
+
+#define BMI088_ACC_TEMP                0x22
+
+#define BMI088_ACC_CONF                0x40
+# define BMI088_ACC_CONF_BWP           4
+# define BMI088_ACC_CONF_BWP_OSR4              0x08
+# define BMI088_ACC_CONF_BWP_OSR2              0x08
+# define BMI088_ACC_CONF_BWP_NORMAL            0x0a
+# define BMI088_ACC_CONF_ODR           0
+# define BMI088_ACC_CONF_ODR_12_5              0x05
+# define BMI088_ACC_CONF_ODR_25                        0x06
+# define BMI088_ACC_CONF_ODR_50                        0x07
+# define BMI088_ACC_CONF_ODR_100               0x08
+# define BMI088_ACC_CONF_ODR_200               0x09
+# define BMI088_ACC_CONF_ODR_400               0x0a
+# define BMI088_ACC_CONF_ODR_800               0x0b
+# define BMI088_ACC_CONF_ODR_1600              0x0c
+
+#define BMI088_ACC_RANGE       0x41
+# define BMI088_ACC_RANGE_3                    0x00
+# define BMI088_ACC_RANGE_6                    0x01
+# define BMI088_ACC_RANGE_12                   0x02
+# define BMI088_ACC_RANGE_24                   0x03
+
+#define BMI088_INT1_IO_CONF    0x53
+# define BMI088_INT1_IO_CONF_INT1_IN   4
+# define BMI088_INT1_IO_CONF_INT1_OUT  3
+# define BMI088_INT1_IO_CONF_INT1_OD   2
+# define BMI088_INT1_IO_CONF_INT1_LVL  1
+
+#define BMI088_INT2_IO_CONF    0x54
+# define BMI088_INT2_IO_CONF_INT2_IN   4
+# define BMI088_INT2_IO_CONF_INT2_OUT  3
+# define BMI088_INT2_IO_CONF_INT2_OD   2
+# define BMI088_INT2_IO_CONF_INT2_LVL  1
+
+#define BMI088_INT2_INT2_MAP_DATA      0x58
+# define BMI088_INT2_INT2_MAP_DATA_INT2_DRDY   6
+# define BMI088_INT2_INT2_MAP_DATA_INT1_DRDY   2
+
+#define BMI088_ACC_SELF_TEST   0x6d
+# define BMI088_ACC_SELF_TEST_OFF      0x00
+# define BMI088_ACC_SELF_TEST_POSITIVE 0x0d
+# define BMI088_ACC_SELF_TEST_NEGATIVE 0x09
+
+#define BMI088_ACC_PWR_CONF    0x7c
+# define BMI088_ACC_PWR_CONF_SUSPEND   0x03
+# define BMI088_ACC_PWR_CONF_ACTIVE    0x00
+
+#define BMI088_ACC_PWR_CTRL    0x7d
+# define BMI088_ACC_PWR_CTRL_OFF       0x00
+# define BMI088_ACC_PWR_CTRL_ON                0x04
+
+#define BMI088_ACC_SOFTRESET   0x7e
+# define BMI088_ACC_SOFTRESET_RESET    0xb6
+
+#define BMI088_GYRO_CHIP_ID    0x00
+# define BMI088_GYRO_CHIP_ID_BMI088    0x0f
+
+#define BMI088_GYRO_DATA       0x02
+#define BMI088_GYRO_DATA_X     0x02
+#define BMI088_GYRO_DATA_Y     0x04
+#define BMI088_GYRO_DATA_Z     0x06
+
+#define BMI088_GYRO_INT_STAT_1 0x0a
+# define BMI088_GYRO_INT_STAT_1_GYRO_DRDY      7
+
+#define BMI088_GYRO_RANGE      0x0f
+# define BMI088_GYRO_RANGE_2000                0x00
+# define BMI088_GYRO_RANGE_1000                0x01
+# define BMI088_GYRO_RANGE_500         0x02
+# define BMI088_GYRO_RANGE_250         0x03
+# define BMI088_GYRO_RANGE_125         0x04
+
+#define BMI088_GYRO_BANDWIDTH  0x10
+# define BMI088_GYRO_BANDWIDTH_2000_532        0x00
+# define BMI088_GYRO_BANDWIDTH_2000_230        0x01
+# define BMI088_GYRO_BANDWIDTH_1000_116        0x02
+# define BMI088_GYRO_BANDWIDTH_400_47  0x03
+# define BMI088_GYRO_BANDWIDTH_200_23  0x04
+# define BMI088_GYRO_BANDWIDTH_100_12  0x05
+# define BMI088_GYRO_BANDWIDTH_200_64  0x06
+# define BMI088_GYRO_BANDWIDTH_100_32  0x07
+
+#define BMI088_GYRO_LPM1       0x11
+# define BMI088_GYRO_LPM1_NORMAL       0x00
+# define BMI088_GYRO_LPM1_SUSPEND      0x80
+# define BMI088_GYRO_LPM1_DEEP_SUSPEND 0x20
+
+#define BMI088_GYRO_SOFTRESET  0x14
+# define BMI088_GYRO_SOFTRESET_RESET   0xb6
+
+#define BMI088_GYRO_INT_CTRL   0x15
+# define BMI088_GYRO_INT_CTRL_DISABLE  0x00
+# define BMI088_GYRO_INT_CTRL_ENABLE   0x80
+
+#define BMI088_INT3_INT4_IO_CONF       0x16
+# define BMI088_INT3_INT4_IO_CONF_INT4_OD      3
+# define BMI088_INT3_INT4_IO_CONF_INT4_LVL     2
+# define BMI088_INT3_INT4_IO_CONF_INT3_OD      1
+# define BMI088_INT3_INT4_IO_CONF_INT3_LVL     0
+
+#define BMI088_INT3_INT4_IO_MAP                0x18
+# define BMI088_INT3_INT4_IO_MAP_NONE          0x00
+# define BMI088_INT3_INT4_IO_MAP_INT3          0x01
+# define BMI088_INT3_INT4_IO_MAP_INT4          0x80
+# define BMI088_INT3_INT4_IO_MAP_INT3_INT4     0x81
+
+#define BMI088_GYRO_SELF_TEST  0x3c
+# define BMI088_GYRO_SELF_TEST_RATE_OK         4
+# define BMI088_GYRO_SELF_TEST_BIST_FAIL       2
+# define BMI088_GYRO_SELF_TEST_BIST_RDY                1
+# define BMI088_GYRO_SELF_TEST_TRIG_BIST       0
+
+#define BMI088_GYRO_FULLSCALE  ((float) 2000.0f * (float) M_PI / 180.0f)
+
+static inline float
+ao_bmi088_gyro(float sensor) {
+       return sensor * ((float) (BMI088_GYRO_FULLSCALE / 32767.0));
+}
+
+#define ao_bmi_gyro_to_sample(gyro) ((gyro) * (32767.0f / (BMI088_GYRO_FULLSCALE
+
+#define BMI088_ACCEL_FULLSCALE 24
+
+static inline float
+ao_bmi088_accel(int16_t sensor) {
+       return (float) sensor * ((float) (BMI088_ACCEL_FULLSCALE * GRAVITY / 32767.0));
+}
+
+#define ao_bmi_accel_to_sample(accel) ((accel) * (32767.0f / (BMI088_ACCEL_FULLSCALE * GRAVITY)))
+
+#endif /* _AO_BMI088_H_ */
index 4ddc96ce09b2dec483e722ea4b03c77ac9858527..727097441007fee784b2e570fd57d7d315ec7466 100644 (file)
@@ -204,6 +204,7 @@ ao_radio_status(void)
 void
 ao_radio_recv_abort(void)
 {
+       ao_exti_disable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
        ao_radio_abort = 1;
        ao_wakeup(&ao_radio_wake);
 }
@@ -213,7 +214,6 @@ ao_radio_recv_abort(void)
 static void
 ao_radio_isr(void)
 {
-       ao_exti_disable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
        ao_radio_wake = 1;
        ao_wakeup(&ao_radio_wake);
 }
@@ -221,14 +221,12 @@ ao_radio_isr(void)
 static void
 ao_radio_start_tx(void)
 {
-       ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
        ao_radio_strobe(CC1200_STX);
 }
 
 static void
 ao_radio_start_rx(void)
 {
-       ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
        ao_radio_strobe(CC1200_SRX);
 }
 
@@ -575,8 +573,7 @@ static uint16_t ao_radio_mode;
 
 #define AO_RADIO_MODE_BITS_PACKET      1
 #define AO_RADIO_MODE_BITS_TX_BUF      4
-#define AO_RADIO_MODE_BITS_TX_FINISH   8
-#define AO_RADIO_MODE_BITS_RX          16
+#define AO_RADIO_MODE_BITS_FINISH      8
 #define AO_RADIO_MODE_BITS_RDF         32
 #define AO_RADIO_MODE_BITS_APRS                64
 #define AO_RADIO_MODE_BITS_TEST                128
@@ -584,12 +581,12 @@ static uint16_t ao_radio_mode;
 #define AO_RADIO_MODE_BITS_FIXED       512
 
 #define AO_RADIO_MODE_NONE             0
-#define AO_RADIO_MODE_PACKET_TX                (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_TX_FINISH)
-#define AO_RADIO_MODE_PACKET_RX                (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_RX)
-#define AO_RADIO_MODE_RDF              (AO_RADIO_MODE_BITS_RDF    | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_TX_FINISH)
+#define AO_RADIO_MODE_PACKET_TX                (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_FINISH)
+#define AO_RADIO_MODE_PACKET_RX                (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_FINISH)
+#define AO_RADIO_MODE_RDF              (AO_RADIO_MODE_BITS_RDF    | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_FINISH)
 #define AO_RADIO_MODE_APRS_BUF         (AO_RADIO_MODE_BITS_APRS   | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF)
 #define AO_RADIO_MODE_APRS_LAST_BUF    (AO_RADIO_MODE_BITS_APRS   | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_TX_BUF)
-#define AO_RADIO_MODE_APRS_FINISH      (AO_RADIO_MODE_BITS_APRS   | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_TX_FINISH)
+#define AO_RADIO_MODE_APRS_FINISH      (AO_RADIO_MODE_BITS_APRS   | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_FINISH)
 #define AO_RADIO_MODE_TEST             (AO_RADIO_MODE_BITS_TEST   | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF)
 
 static void
@@ -642,17 +639,10 @@ ao_radio_set_mode(uint16_t new_mode)
 
        if (changes & AO_RADIO_MODE_BITS_TX_BUF) {
                ao_radio_reg_write(AO_CC1200_INT_GPIO_IOCFG, CC1200_IOCFG_GPIO_CFG_TXFIFO_THR);
-               ao_exti_set_mode(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH);
        }
 
-       if (changes & AO_RADIO_MODE_BITS_TX_FINISH) {
+       if (changes & AO_RADIO_MODE_BITS_FINISH) {
                ao_radio_reg_write(AO_CC1200_INT_GPIO_IOCFG, CC1200_IOCFG_GPIO_CFG_PKT_SYNC_RXTX);
-               ao_exti_set_mode(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH);
-       }
-
-       if (changes & AO_RADIO_MODE_BITS_RX) {
-               ao_radio_reg_write(AO_CC1200_INT_GPIO_IOCFG, CC1200_IOCFG_GPIO_CFG_MARC_MCU_WAKEUP);
-               ao_exti_set_mode(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN, AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_HIGH);
        }
 
        if (changes & AO_RADIO_MODE_BITS_RDF)
@@ -772,12 +762,18 @@ ao_radio_show_state(char *where)
 /* Wait for the radio to signal an interrupt
  */
 static void
-ao_radio_wait_isr(AO_TICK_TYPE timeout)
+_ao_radio_wait_isr(AO_TICK_TYPE timeout)
 {
-       ao_arch_block_interrupts();
        while (!ao_radio_wake && !ao_radio_abort)
                if (ao_sleep_for(&ao_radio_wake, timeout))
                        ao_radio_abort = 1;
+}
+
+static void
+ao_radio_wait_isr(AO_TICK_TYPE timeout)
+{
+       ao_arch_block_interrupts();
+       _ao_radio_wait_isr(timeout);
        ao_arch_release_interrupts();
 }
 
@@ -943,11 +939,12 @@ ao_radio_send_aprs(ao_radio_fill_func fill)
                        ao_radio_set_len((uint8_t) (total & 0xff));
 
                /* Wait for some space in the fifo */
+               ao_arch_block_interrupts();
                while (started && ao_radio_int_pin() != 0 && !ao_radio_abort) {
                        ao_radio_wake = 0;
-                       ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
-                       ao_radio_wait_isr(AO_MS_TO_TICKS(1000));
+                       _ao_radio_wait_isr(AO_MS_TO_TICKS(1000));
                }
+               ao_arch_release_interrupts();
                if (ao_radio_abort)
                        break;
 
@@ -963,11 +960,12 @@ ao_radio_send_aprs(ao_radio_fill_func fill)
                }
        }
        /* Wait for the transmitter to go idle */
+       ao_arch_block_interrupts();
        while (started && ao_radio_int_pin() != 0 && !ao_radio_abort) {
                ao_radio_wake = 0;
-               ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
-               ao_radio_wait_isr(AO_MS_TO_TICKS(1000));
+               _ao_radio_wait_isr(AO_MS_TO_TICKS(1000));
        }
+       ao_arch_release_interrupts();
        if (ao_radio_abort)
                ao_radio_idle();
        ao_radio_put();
@@ -1033,6 +1031,8 @@ ao_radio_recv(void *d, uint8_t size, AO_TICK_TYPE timeout)
 
        while (!ao_radio_abort) {
                ao_radio_wait_isr(timeout);
+               if (ao_radio_abort)
+                       break;
                if (ao_radio_wake) {
                        uint8_t         marc_status1 = ao_radio_reg_read(CC1200_MARC_STATUS1);
 
@@ -1481,5 +1481,7 @@ ao_radio_init(void)
                      AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
                      ao_radio_isr);
 
+       ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
+
        ao_cmd_register(&ao_radio_cmds[0]);
 }
index 3a253eea69559fb19731824c46866995e5b75baa..00c6932fc646e954d4f65c5263501632c5f4fd47 100644 (file)
@@ -40,6 +40,7 @@ static uint8_t ao_gps_cksum;
 static uint8_t ao_gps_error;
 
 AO_TICK_TYPE ao_gps_tick;
+AO_TICK_TYPE ao_gps_utc_tick;
 struct ao_telemetry_location   ao_gps_data;
 struct ao_telemetry_satellite  ao_gps_tracking_data;
 
@@ -296,7 +297,7 @@ ao_nmea_gga(void)
        if (!ao_gps_error) {
                ao_mutex_get(&ao_gps_mutex);
                ao_gps_new |= AO_GPS_NEW_DATA;
-               ao_gps_tick = ao_gps_next_tick;
+               ao_gps_tick = ao_gps_utc_tick = ao_gps_next_tick;
                memcpy(&ao_gps_data, &ao_gps_next, sizeof (ao_gps_data));
                ao_mutex_put(&ao_gps_mutex);
                ao_wakeup(&ao_gps_new);
index 57cbf22a77b50245975955925096ba85ac9723f5..3d615e9c933f14b260f3cd0fb75faeec68073a3c 100644 (file)
@@ -29,6 +29,7 @@
 uint8_t ao_gps_new;
 uint8_t ao_gps_mutex;
 AO_TICK_TYPE ao_gps_tick;
+AO_TICK_TYPE ao_gps_utc_tick;
 struct ao_telemetry_location   ao_gps_data;
 struct ao_telemetry_satellite  ao_gps_tracking_data;
 
@@ -63,12 +64,13 @@ static uint16_t ao_ublox_len;
 
 #if AO_UBLOX_DEBUG
 
-static uint8_t ao_gps_dbg_enable;
 
 #define DBG_PROTO      1
 #define DBG_CHAR       2
 #define DBG_INIT       4
 
+static uint8_t ao_gps_dbg_enable = DBG_PROTO|DBG_CHAR|DBG_INIT;
+
 static void ao_gps_dbg(int level, char *format, ...) {
        va_list a;
 
@@ -397,6 +399,7 @@ ao_ublox_parse_nav_svinfo(void)
  * NAV-TIMEUTC message parsing
  */
 static struct nav_timeutc {
+       int32_t         nano;
        uint16_t        year;
        uint8_t         month;
        uint8_t         day;
@@ -411,7 +414,8 @@ static struct nav_timeutc {
 #define NAV_TIMEUTC_VALID_UTC  2
 
 static const struct ublox_packet_parse nav_timeutc_packet[] = {
-       { UBLOX_DISCARD, 12 },                                          /* 0 iTOW, tAcc, nano */
+       { UBLOX_DISCARD, 8 },                                           /* 0 iTOW, tAcc */
+       { UBLOX_U32, offsetof(struct nav_timeutc, nano) },              /* 8 nano */
        { UBLOX_U16, offsetof(struct nav_timeutc, year) },              /* 12 year */
        { UBLOX_U8, offsetof(struct nav_timeutc, month) },              /* 14 month */
        { UBLOX_U8, offsetof(struct nav_timeutc, day) },                /* 15 day */
@@ -721,7 +725,7 @@ ao_gps(void)
                        case UBLOX_NAV_TIMEUTC:
                                ao_mutex_get(&ao_gps_mutex);
                                ao_gps_tick = solution_tick;
-
+                               ao_gps_utc_tick = packet_start_tick + (AO_TICK_TYPE) AO_NS_TO_TICKS(nav_timeutc.nano);
                                ao_gps_data.flags = 0;
                                ao_gps_data.flags |= AO_GPS_RUNNING;
                                if (nav_sol.gps_fix & (1 << NAV_SOL_FLAGS_GPSFIXOK)) {
@@ -788,7 +792,7 @@ ao_gps(void)
 #if AO_UBLOX_DEBUG
 static void ao_gps_option(void)
 {
-       uint16_t r = ao_cmd_hex();
+       uint8_t r = (uint8_t) ao_cmd_hex();
        if (ao_cmd_status != ao_cmd_success) {
                ao_cmd_status = ao_cmd_success;
                ao_gps_show();
index 65e9a8c9cb76c4a70a6650eb7145a75682056885..03c810be49fe7f0bfe6a9210323ff342735ad93d 100644 (file)
@@ -87,6 +87,9 @@ ao_lco_set_armed(uint8_t armed);
 void
 ao_lco_set_firing(uint8_t firing);
 
+void
+ao_lco_pretend(void);
+
 void
 ao_lco_toggle_drag(void);
 
index c88170b8c91dec772d36747715b955978d502de0..acdea87e3328759d8b292864ee02e28be3497e64 100644 (file)
@@ -26,6 +26,8 @@ uint8_t               ao_lco_firing;                                  /* fire active */
 
 uint16_t       ao_lco_min_box, ao_lco_max_box;
 
+uint8_t                ao_lco_pretending;
+
 #if AO_LCO_DRAG
 uint8_t                ao_lco_drag_race;
 #endif
@@ -197,6 +199,7 @@ ao_lco_box_reset_present(void)
 {
        ao_lco_min_box = 0xff;
        ao_lco_max_box = 0x00;
+       ao_lco_pretending = 0;
        memset(ao_lco_box_mask, 0, sizeof (ao_lco_box_mask));
 }
 
@@ -223,8 +226,12 @@ void
 ao_lco_set_box(uint16_t new_box)
 {
        ao_lco_box = new_box;
-       if (ao_lco_box < AO_PAD_MAX_BOXES)
-               ao_lco_channels[ao_lco_box] = 0;
+       if (ao_lco_box < AO_PAD_MAX_BOXES) {
+               if (ao_lco_pretending)
+                       ao_lco_channels[ao_lco_box] = 0xff;
+               else
+                       ao_lco_channels[ao_lco_box] = 0;
+       }
        ao_lco_pad = 1;
        ao_lco_show();
 }
@@ -288,8 +295,8 @@ ao_lco_search(void)
 {
        int8_t          r;
        int8_t          try;
-       uint8_t         box;
-       uint8_t         boxes = 0;
+       uint16_t        box;
+       uint16_t        boxes = 0;
 
        ao_lco_box_reset_present();
        ao_lco_show_box(0);
@@ -304,7 +311,7 @@ ao_lco_search(void)
                        if (r == AO_RADIO_CMAC_OK) {
                                ++boxes;
                                ao_lco_box_set_present(box);
-                               ao_lco_show_pad(boxes % 10);
+                               ao_lco_show_pad((uint8_t) (boxes % 10));
                                ao_delay(AO_MS_TO_TICKS(30));
                                break;
                        }
@@ -319,6 +326,22 @@ ao_lco_search(void)
        ao_lco_set_box(ao_lco_min_box);
 }
 
+void
+ao_lco_pretend(void)
+{
+       uint16_t box;
+
+       ao_lco_pretending = 1;
+       ao_lco_min_box = 1;
+       ao_lco_max_box = AO_PAD_MAX_BOXES - 1;
+       for (box = ao_lco_min_box; box < ao_lco_max_box; box++)
+               ao_lco_box_set_present(box);
+       ao_lco_box = ao_lco_min_box;
+       memset(ao_lco_valid, 0, sizeof (ao_lco_valid));
+       memset(ao_lco_channels, 0, sizeof (ao_lco_channels));
+       ao_lco_set_box(ao_lco_min_box);
+}
+
 void
 ao_lco_monitor(void)
 {
index b32034e7d3f2f0903a03751e7c8210ffc17462ef..009e9bc23e434cd8496cd5177eb62257c299c805 100644 (file)
@@ -20,7 +20,7 @@
 
 static const struct {
        void            *port;
-       uint16_t        pin;
+       uint8_t         pin;
 } ao_leds[] = {
 #ifdef LED_0_PORT
     [0] { LED_0_PORT, LED_0_PIN },
index f73b095622cd5a4eccf9f1d0b7e28a7ade50caf7..dce7c0e6a3c5a5de8af7ccf570ee6b43c0c5f9b9 100644 (file)
@@ -148,43 +148,49 @@ static void
 ao_ms5607_isr(void)
 {
        ao_exti_disable(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN);
-       ao_ms5607_done = 1;
        ao_wakeup((void *) &ao_ms5607_done);
 }
 
 static uint32_t
 ao_ms5607_get_sample(uint8_t cmd) {
-       uint8_t reply[3];
-       uint8_t read;
-
-       ao_ms5607_done = 0;
+       uint8_t reply[4];
 
        ao_ms5607_start();
-       ao_spi_send(&cmd, 1, AO_MS5607_SPI_INDEX);
 
        ao_exti_enable(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN);
 
+       ao_spi_send(&cmd, 1, AO_MS5607_SPI_INDEX);
+
 #if AO_MS5607_PRIVATE_PINS
-       ao_spi_put(AO_MS5607_SPI_INDEX);
+       ao_spi_put_pins(AO_MS5607_SPI_INDEX);
 #endif
        ao_arch_block_interrupts();
-       while (!ao_gpio_get(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN) &&
-              !ao_ms5607_done)
-               ao_sleep((void *) &ao_ms5607_done);
+#if !HAS_TASK
+#define ao_sleep_for(a,t) ao_sleep(a)
+#endif
+       while (!ao_gpio_get(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN)) {
+               if (ao_sleep_for((void *) &ao_ms5607_done, AO_MS_TO_TICKS(10)))
+                       break;
+       }
+       ao_exti_disable(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN);
        ao_arch_release_interrupts();
 #if AO_MS5607_PRIVATE_PINS
-       stm_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 1);
+       ao_spi_clr_cs(AO_MS5607_CS_PORT, 1 << (AO_MS5607_CS_PIN));
 #else
        ao_ms5607_stop();
 #endif
 
        ao_ms5607_start();
-       read = AO_MS5607_ADC_READ;
-       ao_spi_send(&read, 1, AO_MS5607_SPI_INDEX);
-       ao_spi_recv(&reply, 3, AO_MS5607_SPI_INDEX);
+       reply[0] = AO_MS5607_ADC_READ;
+#if defined(AO_SPI_DUPLEX) && AO_SPI_DUPLEX == 0
+       ao_spi_send(reply, 1, AO_MS5607_SPI_INDEX);
+       ao_spi_recv(reply+1, 3, AO_MS5607_SPI_INDEX);
+#else
+       ao_spi_duplex(&reply, &reply, 4, AO_MS5607_SPI_INDEX);
+#endif
        ao_ms5607_stop();
 
-       return ((uint32_t) reply[0] << 16) | ((uint32_t) reply[1] << 8) | (uint32_t) reply[2];
+       return ((uint32_t) reply[1] << 16) | ((uint32_t) reply[2] << 8) | (uint32_t) reply[3];
 }
 
 #ifndef AO_MS5607_BARO_OVERSAMPLE
diff --git a/src/easymotor-v3/.gitignore b/src/easymotor-v3/.gitignore
new file mode 100644 (file)
index 0000000..e5f7d58
--- /dev/null
@@ -0,0 +1,2 @@
+ao_product.h
+*.elf
index b33572a8fbe070f80d539a21f66b47a3f24e92a3..3278d01e4af06a462d90a36f68ee723500564ebd 100644 (file)
@@ -28,8 +28,7 @@ ALTOS_SRC = \
        ao_stdio.c \
        ao_storage.c \
        ao_report.c \
-       ao_flight.c \
-       ao_kalman.c \
+       ao_motor_flight.c \
        ao_sample.c \
        ao_data.c \
        ao_convert_volt.c \
@@ -70,7 +69,7 @@ $(OBJ): $(INC)
 distclean:     clean
 
 clean:
-       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
        rm -f ao_product.h
 
 install:
index 856c763946d1b6dcd7aedd15e0b1e2a661656e2b..a0e40bd7ba5d326a7591d4da75bd13dfef5fed79 100644 (file)
 
 #define AO_DATA_RING           32
 
-struct ao_adc {
-       int16_t                 v_batt;
-       int16_t                 motor_pressure;
-};
 
 #define AO_ADC_DUMP(p) \
        printf("tick: %5lu batt: %5d motor_pressure: %5d\n", \
@@ -152,10 +148,26 @@ struct ao_adc {
 #define HAS_IMU                        1
 #define USE_ADXL375_IMU                1
 
+#define HAS_KALMAN             0
+
 /* Motor pressure */
 #define HAS_MOTOR_PRESSURE     1
 #define ao_data_motor_pressure(packet) ((packet)->adc.motor_pressure)
 
 typedef int16_t        motor_pressure_t;
 
+/* want about 50psi, or 344kPa for boost and 30psi for coast */
+
+#define AO_FULL_SCALE_PRESSURE         11031612        /* 1600psi */
+#define AO_BOOST_DETECT_PRESSURE       344000          /* 50psi */
+#define AO_QUIET_DETECT_PRESSURE       207000          /* 30psi */
+
+#define AO_PRESSURE_VOLTS_BASE 0.5
+#define AO_PRESSURE_VOLTS_MAX  4.5
+
+struct ao_adc {
+       int16_t                 v_batt;
+       int16_t                 motor_pressure;
+};
+
 #endif /* _AO_PINS_H_ */
index 9ca2c60eac711c94734d199eca9603ac98ec5169..d714cecbd15e508c3705c0e6751ecae5db2bb475 100644 (file)
@@ -113,6 +113,7 @@ extern volatile AO_TICK_TYPE ao_tick_count;
 #endif
 #define AO_MS_TO_TICKS(ms)     ((ms) / (1000 / AO_HERTZ))
 #define AO_SEC_TO_TICKS(s)     ((AO_TICK_TYPE) (s) * AO_HERTZ)
+#define AO_NS_TO_TICKS(ns)     ((ns) / (1000000000L / AO_HERTZ))
 
 /* Returns the current time in ticks */
 AO_TICK_TYPE
@@ -390,6 +391,7 @@ ao_spi_slave(void);
 
 extern uint8_t ao_gps_new;
 extern AO_TICK_TYPE ao_gps_tick;
+extern AO_TICK_TYPE ao_gps_utc_tick;
 extern uint8_t ao_gps_mutex;
 extern struct ao_telemetry_location ao_gps_data;
 extern struct ao_telemetry_satellite ao_gps_tracking_data;
index b5dd7ddf8f1c32f47de032be6d65d8904b43944e..be3e8c7222d1bc48746700a8f1ac32d0d7ac6daf 100644 (file)
@@ -55,6 +55,7 @@ uint8_t ao_force_freq;
 #define AO_CONFIG_DEFAULT_PAD_ORIENTATION      AO_PAD_ORIENTATION_ANTENNA_UP
 #define AO_CONFIG_DEFAULT_PYRO_TIME    AO_MS_TO_TICKS(50)
 #define AO_CONFIG_DEFAULT_RADIO_10MW   0
+#define AO_CONFIG_DEFAULT_REPORT_FEET  0
 #if HAS_CONFIG_SAVE
 #ifndef USE_INTERNAL_FLASH
 #error Please define USE_INTERNAL_FLASH
@@ -251,6 +252,8 @@ _ao_config_get(void)
                if (minor < 25)
                        ao_config.radio_10mw = AO_CONFIG_DEFAULT_RADIO_10MW;
 #endif
+               if (minor < 26)
+                       ao_config.report_feet = AO_CONFIG_DEFAULT_REPORT_FEET;
                ao_config.minor = AO_CONFIG_MINOR;
                ao_config_dirty = 1;
        }
@@ -880,6 +883,24 @@ ao_config_radio_10mw_set(void)
 
 #endif
 
+static void
+ao_config_report_feet_show(void)
+{
+       printf ("Report in feet: %d\n", ao_config.report_feet);
+}
+
+static void
+ao_config_report_feet_set(void)
+{
+       uint32_t r = ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_config.report_feet = !!r;
+       _ao_config_edit_finish();
+}
+
+
 #if HAS_BEEP
 static void
 ao_config_beep_show(void)
@@ -1131,6 +1152,8 @@ const struct ao_config_var ao_config_vars[] = {
        { "p <0 no limit, 1 limit>\0Limit radio power to 10mW",
          ao_config_radio_10mw_set,     ao_config_radio_10mw_show },
 #endif
+       { "u <0 meters, 1 feet>\0Units to report height after landing",
+         ao_config_report_feet_set,    ao_config_report_feet_show },
        { "s\0Show",
          ao_config_show,               0 },
 #if HAS_CONFIG_SAVE
index f43401be6051a812f0c9854a408f832a64b83ed9..bf57d9543a11102f09dc3d29bdfb4bdf5ffe2b7b 100644 (file)
@@ -58,7 +58,7 @@
 #endif
 
 #define AO_CONFIG_MAJOR        1
-#define AO_CONFIG_MINOR        25
+#define AO_CONFIG_MINOR        26
 
 /* All cc1200 devices support limiting TX power to 10mW */
 #if !defined(HAS_RADIO_10MW) && defined(AO_CC1200_SPI)
@@ -134,6 +134,7 @@ struct ao_config {
 #if HAS_RADIO_10MW
        uint8_t         radio_10mw;             /* minor version 25 */
 #endif
+       uint8_t         report_feet;            /* minor version 26 */
 };
 
 struct ao_config_1_24 {
index 0f96cb897bcf521081c89e6007cc56444697ac73..8545c7d4ddfabfa10763d4b79095ef6bf410ad54 100644 (file)
 #define AO_DATA_BMX160 0
 #endif
 
+#if HAS_BMI088
+#include <ao_bmi088.h>
+#define AO_DATA_BMI088 (1 << 2)
+#else
+#define AO_DATA_BMI088 0
+#endif
+
 #ifndef HAS_SENSOR_ERRORS
 #if HAS_IMU || HAS_MMA655X || HAS_MS5607 || HAS_MS5611
 #define HAS_SENSOR_ERRORS      1
@@ -102,7 +109,7 @@ extern uint8_t                      ao_sensor_errors;
 
 #ifdef AO_DATA_RING
 
-#define AO_DATA_ALL    (AO_DATA_ADC|AO_DATA_MS5607|AO_DATA_MPU6000|AO_DATA_HMC5883|AO_DATA_MMA655X|AO_DATA_MPU9250|AO_DATA_ADXL375|AO_DATA_BMX160|AO_DATA_MMC5983)
+#define AO_DATA_ALL    (AO_DATA_ADC|AO_DATA_MS5607|AO_DATA_MPU6000|AO_DATA_HMC5883|AO_DATA_MMA655X|AO_DATA_MPU9250|AO_DATA_ADXL375|AO_DATA_BMX160|AO_DATA_MMC5983|AO_DATA_BMI088)
 
 struct ao_data {
        AO_TICK_TYPE                    tick;
@@ -149,6 +156,12 @@ struct ao_data {
        int16_t z_accel;
 #endif
 #endif
+#if HAS_BMI088
+       struct ao_bmi088_sample         bmi088;
+#if !HAS_ADXL375
+       int16_t z_accel;
+#endif
+#endif
 };
 
 #define ao_data_ring_next(n)   (((n) + 1) & (AO_DATA_RING - 1))
@@ -182,6 +195,10 @@ extern volatile uint8_t            ao_data_count;
 
 #endif /* AO_DATA_RING */
 
+#define AO_ALT_TYPE    int32_t
+
+typedef AO_ALT_TYPE    alt_t;
+
 #if !HAS_BARO && HAS_MS5607
 
 /* Either an MS5607 or an MS5611 hooked to a SPI port
@@ -191,10 +208,6 @@ extern volatile uint8_t            ao_data_count;
 
 typedef int32_t        pres_t;
 
-#define AO_ALT_TYPE    int32_t
-
-typedef AO_ALT_TYPE    alt_t;
-
 #define ao_data_pres_cook(packet)      ao_ms5607_convert(&packet->ms5607_raw, &packet->ms5607_cooked)
 
 #define ao_data_pres(packet)   ((packet)->ms5607_cooked.pres)
@@ -490,6 +503,29 @@ static inline float ao_convert_accel(int16_t sensor)
 
 #endif
 
+#if !HAS_GYRO && HAS_BMI088
+
+#define HAS_GYRO       1
+
+typedef int16_t        gyro_t;         /* in raw sample units */
+typedef int16_t angle_t;       /* in degrees */
+
+/* X axis is aligned with the direction of motion (along) */
+/* Y axis is aligned in the other board axis (across) */
+/* Z axis is aligned perpendicular to the board (through) */
+
+static inline float ao_convert_gyro(float sensor)
+{
+       return ao_bmi088_gyro(sensor);
+}
+
+static inline float ao_convert_accel(int16_t sensor)
+{
+       return ao_bmi088_accel(sensor);
+}
+
+#endif
+
 #if !HAS_MAG && HAS_HMC5883
 
 #define HAS_MAG                1
@@ -563,6 +599,9 @@ ao_data_fill(int head) {
 #endif
 #if HAS_BMX160
                ao_data_ring[head].bmx160 = ao_bmx160_current;
+#endif
+#if HAS_BMI088
+               ao_data_ring[head].bmi088 = ao_bmi088_current;
 #endif
                ao_data_ring[head].tick = ao_tick_count;
                ao_data_head = ao_data_ring_next(head);
index 1faea957639ce667ad44895b60dd22bf52b893d7..9df34b4e566cf383daf1c573e4458c9da1778afe 100644 (file)
@@ -61,6 +61,7 @@ extern enum ao_flight_state ao_log_state;
 #define AO_LOG_FORMAT_TELEMEGA_4       19      /* 32 byte typed telemega records with 32 bit gyro cal and Bmx160 */
 #define AO_LOG_FORMAT_EASYMOTOR                20      /* 16 byte typed easymotor records with pressure sensor and adxl375 */
 #define AO_LOG_FORMAT_TELEMEGA_5       21      /* 32 byte typed telemega records with 32 bit gyro cal, mpu6000 and mmc5983 */
+#define AO_LOG_FORMAT_TELEMEGA_6       22      /* 32 byte typed telemega records with 32 bit gyro cal, bmi088 and mmc5983 */
 #define AO_LOG_FORMAT_NONE             127     /* No log at all */
 
 /* Return the flight number from the given log slot, 0 if none, -slot on failure */
@@ -549,7 +550,7 @@ struct ao_log_motor {
        } u;
 };
 
-#if AO_LOG_FORMAT == AO_LOG_FOMAT_TELEMEGA_OLD || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_3 || AO_LOG_FORMAT == AO_LOG_FORMAT_EASYMEGA_2 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_4 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_5
+#if AO_LOG_FORMAT == AO_LOG_FOMAT_TELEMEGA_OLD || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_3 || AO_LOG_FORMAT == AO_LOG_FORMAT_EASYMEGA_2 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_4 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_5 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_6
 typedef struct ao_log_mega ao_log_type;
 #endif
 
index 432f1b851c7918c00180db1cb4748acc1ab473df..ca7bc8bbaa1272d76bac21f0021399f050c5724f 100644 (file)
@@ -83,15 +83,23 @@ ao_log(void)
                                ao_log_data.u.sensor.pres = d->ms5607_raw.pres;
                                ao_log_data.u.sensor.temp = d->ms5607_raw.temp;
 #endif
-#if HAS_MPU6000
+
 #ifdef AO_LOG_NORMALIZED
+# if HAS_IMU
                                ao_log_data.u.sensor.accel_along = ao_data_along(d);
                                ao_log_data.u.sensor.accel_across = ao_data_across(d);
                                ao_log_data.u.sensor.accel_through = ao_data_through(d);
                                ao_log_data.u.sensor.gyro_roll = ao_data_roll(d);
                                ao_log_data.u.sensor.gyro_pitch = ao_data_pitch(d);
                                ao_log_data.u.sensor.gyro_yaw = ao_data_yaw(d);
-#else
+# endif
+# if HAS_MAG
+                               ao_log_data.u.sensor.mag_along = ao_data_mag_along(d);
+                               ao_log_data.u.sensor.mag_across = ao_data_mag_across(d);
+                               ao_log_data.u.sensor.mag_through = ao_data_mag_through(d);
+# endif
+#else  /* AO_LOG_NORMALIZED */
+#if HAS_MPU6000
                                ao_log_data.u.sensor.accel_x = d->mpu6000.accel_x;
                                ao_log_data.u.sensor.accel_y = d->mpu6000.accel_y;
                                ao_log_data.u.sensor.accel_z = d->mpu6000.accel_z;
@@ -99,17 +107,11 @@ ao_log(void)
                                ao_log_data.u.sensor.gyro_y = d->mpu6000.gyro_y;
                                ao_log_data.u.sensor.gyro_z = d->mpu6000.gyro_z;
 #endif
-#endif
 #if HAS_HMC5883
                                ao_log_data.u.sensor.mag_x = d->hmc5883.x;
                                ao_log_data.u.sensor.mag_z = d->hmc5883.z;
                                ao_log_data.u.sensor.mag_y = d->hmc5883.y;
 #endif
-#ifdef HAS_MMC5983
-                               ao_log_data.u.sensor.mag_along = ao_data_mag_along(d);
-                               ao_log_data.u.sensor.mag_across = ao_data_mag_across(d);
-                               ao_log_data.u.sensor.mag_through = ao_data_mag_through(d);
-#endif
 #if HAS_MPU9250
                                ao_log_data.u.sensor.accel_x = d->mpu9250.accel_x;
                                ao_log_data.u.sensor.accel_y = d->mpu9250.accel_y;
@@ -132,6 +134,7 @@ ao_log(void)
                                ao_log_data.u.sensor.mag_z = d->bmx160.mag_z;
                                ao_log_data.u.sensor.mag_y = d->bmx160.mag_y;
 #endif
+#endif /* !AO_LOG_NORMALIZED */
                                ao_log_data.u.sensor.accel = ao_data_accel(&ao_data_ring[ao_log_data_pos]);
                                ao_log_write(&ao_log_data);
                                if (ao_log_state <= ao_flight_coast)
diff --git a/src/kernel/ao_motor_flight.c b/src/kernel/ao_motor_flight.c
new file mode 100644 (file)
index 0000000..6fa6883
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef AO_FLIGHT_TEST
+#include "ao.h"
+#include <ao_log.h>
+#endif
+
+#include <ao_flight.h>
+
+/* Main flight thread. */
+
+enum ao_flight_state   ao_flight_state;        /* current flight state */
+AO_TICK_TYPE           ao_launch_tick;         /* time of first boost detect */
+
+#if HAS_SENSOR_ERRORS
+/* Any sensor can set this to mark the flight computer as 'broken' */
+uint8_t                        ao_sensor_errors;
+#endif
+
+/*
+ * track min/max data over a long interval to detect
+ * resting
+ */
+static AO_TICK_TYPE    ao_interval_end;
+
+#define init_bounds(_cur, _min, _max) do {                             \
+               _min = _max = _cur;                                     \
+       } while (0)
+
+#define check_bounds(_cur, _min, _max) do {    \
+               if (_cur < _min)                \
+                       _min = _cur;            \
+               if (_cur > _max)                \
+                       _max = _cur;            \
+       } while(0)
+
+uint8_t                        ao_flight_force_idle;
+
+/* Compute ADC value change given a defined pressure change in Pa */
+
+static inline int16_t
+ao_delta_pressure_to_adc(uint32_t pressure)
+{
+       static const double volts_base = AO_PRESSURE_VOLTS_BASE;
+       static const double volts_max = AO_PRESSURE_VOLTS_MAX;
+
+       /* Compute change in voltage from the sensor */
+       double  volts = (double) pressure / AO_FULL_SCALE_PRESSURE * (volts_max - volts_base);
+
+       /* voltage divider in front of the ADC input to decivolts */
+       double  adc_dv = volts * (10.0 * (double) AO_PRESSURE_DIV_MINUS /
+                                 ((double) AO_PRESSURE_DIV_PLUS + (double) AO_PRESSURE_DIV_MINUS));
+
+       /* convert to ADC output value */
+       double  adc = adc_dv * AO_ADC_MAX / AO_ADC_REFERENCE_DV;
+
+       if (adc > AO_ADC_MAX)
+               adc = AO_ADC_MAX;
+       if (adc < 0)
+               adc = 0;
+
+       return (int16_t) adc;
+}
+
+#define AO_BOOST_DETECT                        ao_delta_pressure_to_adc(AO_BOOST_DETECT_PRESSURE)
+#define AO_QUIET_DETECT                        ao_delta_pressure_to_adc(AO_QUIET_DETECT_PRESSURE)
+
+/*
+ * Landing is detected by getting constant readings from pressure sensor
+ * for a fairly long time (AO_INTERVAL_TICKS), along with the max being
+ * less than the boost detect pressure
+ */
+#define AO_INTERVAL_TICKS      AO_SEC_TO_TICKS(10)
+
+static AO_TICK_TYPE            ao_interval_end;
+static motor_pressure_t                ao_interval_min_motor_pressure, ao_interval_max_motor_pressure;
+
+#define abs(a) ((a) < 0 ? -(a) : (a))
+
+void
+ao_flight(void)
+{
+       ao_sample_init();
+       ao_flight_state = ao_flight_startup;
+       for (;;) {
+
+               /*
+                * Process ADC samples, just looping
+                * until the sensors are calibrated.
+                */
+               if (!ao_sample())
+                       continue;
+
+               switch (ao_flight_state) {
+               case ao_flight_startup:
+
+                       if (!ao_flight_force_idle)
+                       {
+                               /* Set pad mode - we can fly! */
+                               ao_flight_state = ao_flight_pad;
+#if HAS_USB && !HAS_FLIGHT_DEBUG && !HAS_SAMPLE_PROFILE && !DEBUG
+                               /* Disable the USB controller in flight mode
+                                * to save power
+                                */
+#if HAS_FAKE_FLIGHT
+                               if (!ao_fake_flight_active)
+#endif
+                                       ao_usb_disable();
+#endif
+
+#if AO_LED_RED
+                               /* signal successful initialization by turning off the LED */
+                               ao_led_off(AO_LED_RED);
+#endif
+                       } else {
+                               /* Set idle mode */
+                               ao_flight_state = ao_flight_idle;
+#if HAS_SENSOR_ERRORS
+                               if (ao_sensor_errors)
+                                       ao_flight_state = ao_flight_invalid;
+#endif
+
+#if AO_LED_RED
+                               /* signal successful initialization by turning off the LED */
+                               ao_led_off(AO_LED_RED);
+#endif
+                       }
+                       /* wakeup threads due to state change */
+                       ao_wakeup(&ao_flight_state);
+
+                       break;
+
+               case ao_flight_pad:
+                       /* pad to boost:
+                        *
+                        * motor pressure rise > 50psi
+                        */
+                       if (ao_sample_motor_pressure - ao_ground_motor_pressure >= AO_BOOST_DETECT) 
+                       {
+                               ao_flight_state = ao_flight_boost;
+                               ao_wakeup(&ao_flight_state);
+
+                               ao_launch_tick = ao_sample_tick;
+
+                               /* start logging data */
+#if HAS_LOG
+                               ao_log_start();
+#endif
+                               /* Initialize landing detection interval values */
+                               ao_interval_end = ao_sample_tick + AO_INTERVAL_TICKS;
+
+                               init_bounds(ao_sample_motor_pressure, ao_interval_min_motor_pressure, ao_interval_max_motor_pressure);
+                       }
+                       break;
+               case ao_flight_boost:
+                       /* boost to landed:
+                        *
+                        * motor pressure low and stable for more than 10 seconds
+                        */
+                       check_bounds(ao_sample_motor_pressure, ao_interval_min_motor_pressure,
+                                    ao_interval_max_motor_pressure);
+
+                       if ((AO_TICK_SIGNED) (ao_sample_tick - ao_interval_end) >= 0) {
+                               if (ao_interval_max_motor_pressure - ao_ground_motor_pressure <= AO_BOOST_DETECT &&
+                                   ao_interval_max_motor_pressure - ao_interval_min_motor_pressure <= AO_QUIET_DETECT_PRESSURE)
+                               {
+                                       ao_flight_state = ao_flight_landed;
+                                       ao_wakeup(&ao_flight_state);
+#if HAS_ADC
+                                       /* turn off the ADC capture */
+                                       ao_timer_set_adc_interval(0);
+#endif
+                               }
+
+                               /* Reset interval values */
+                               ao_interval_end = ao_sample_tick + AO_INTERVAL_TICKS;
+
+                               init_bounds(ao_sample_motor_pressure, ao_interval_min_motor_pressure, ao_interval_max_motor_pressure);
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+#if HAS_FLIGHT_DEBUG
+static inline int int_part(ao_v_t i)   { return i >> 4; }
+static inline int frac_part(ao_v_t i)  { return ((i & 0xf) * 100 + 8) / 16; }
+
+static void
+ao_flight_dump(void)
+{
+#if HAS_ACCEL
+       ao_v_t  accel;
+
+       accel = ((ao_config.accel_plus_g - ao_sample_accel) * ao_accel_scale) >> 16;
+#endif
+
+       printf ("sample:\n");
+       printf ("  tick        %d\n", ao_sample_tick);
+#if HAS_BARO
+       printf ("  raw pres    %ld\n", ao_sample_pres);
+#endif
+#if HAS_ACCEL
+       printf ("  raw accel   %d\n", ao_sample_accel);
+#endif
+#if HAS_BARO
+       printf ("  ground pres %ld\n", ao_ground_pres);
+       printf ("  ground alt  %ld\n", ao_ground_height);
+#endif
+#if HAS_ACCEL
+       printf ("  raw accel   %d\n", ao_sample_accel);
+       printf ("  groundaccel %d\n", ao_ground_accel);
+       printf ("  accel_2g    %d\n", ao_accel_2g);
+#endif
+
+#if HAS_BARO
+       printf ("  alt         %ld\n", ao_sample_alt);
+       printf ("  height      %ld\n", ao_sample_height);
+#endif
+
+#if HAS_ACCEL
+       printf ("  accel       %d.%02d\n", int_part(accel), frac_part(accel));
+#endif
+
+
+       printf ("kalman:\n");
+       printf ("  height      %ld\n", ao_height);
+       printf ("  speed       %d.%02d\n", int_part(ao_speed), frac_part(ao_speed));
+       printf ("  accel       %d.%02d\n", int_part(ao_accel), frac_part(ao_accel));
+       printf ("  max_height  %ld\n", ao_max_height);
+       printf ("  avg_height  %ld\n", ao_avg_height);
+       printf ("  error_h     %ld\n", ao_error_h);
+#if !HAS_ACCEL
+       printf ("  error_avg   %d\n", ao_error_h_sq_avg);
+#endif
+}
+
+static void
+ao_gyro_test(void)
+{
+       ao_flight_state = ao_flight_test;
+       ao_getchar();
+       ao_flight_state = ao_flight_idle;
+}
+
+uint8_t ao_orient_test;
+
+static void
+ao_orient_test_select(void)
+{
+       ao_orient_test = !ao_orient_test;
+       printf("orient test %d\n", ao_orient_test);
+}
+
+const struct ao_cmds ao_flight_cmds[] = {
+       { ao_flight_dump,       "F\0Dump flight status" },
+       { ao_gyro_test,         "G\0Test gyro code" },
+       { ao_orient_test_select,"O\0Test orientation code" },
+       { 0, NULL },
+};
+#endif
+
+static struct ao_task  flight_task;
+
+void
+ao_flight_init(void)
+{
+       ao_flight_state = ao_flight_startup;
+#if HAS_FLIGHT_DEBUG
+       ao_cmd_register(&ao_flight_cmds[0]);
+#endif
+       ao_add_task(&flight_task, ao_flight, "flight");
+}
index 4624403a10d61e0a255caf38f56fecdc8bcff4af..3795eeb34df54ed02bb96e52fb313b8fe0fcc01a 100644 (file)
@@ -149,7 +149,7 @@ ao_report_digit(uint8_t digit)
 }
 
 static void
-ao_report_number(ao_v_t n)
+ao_report_number(int32_t n)
 {
        uint8_t digits[10];
        uint8_t ndigits, i;
@@ -168,11 +168,20 @@ ao_report_number(ao_v_t n)
        while (i != 0);
 }
 
+#ifdef HAS_BARO
 static void
 ao_report_altitude(void)
 {
-       ao_report_number(ao_max_height);
+       alt_t max_h = ao_max_height;
+       if (ao_config.report_feet) {
+               max_h = max_h * 39 / 12;
+               /* report a leading zero to distinguish */
+               if (max_h)
+                       ao_report_digit(0);
+       }
+       ao_report_number(max_h);
 }
+#endif
 
 #if HAS_BATTERY_REPORT
 static void
@@ -269,7 +278,9 @@ ao_report(void)
 #endif
 
                if (ao_report_state == ao_flight_landed) {
+#if HAS_BARO
                        ao_report_altitude();
+#endif
 #if HAS_FLIGHT
                        ao_delay(AO_SEC_TO_TICKS(5));
                        continue;
index 3f5fc7a98c557af012d053dd0b14068305f6924b..422ccec610332c78fc8286cf26670c6c5a62fd1f 100644 (file)
 #include <ao_data.h>
 #endif
 
+#ifndef HAS_KALMAN
+#define HAS_KALMAN 1
+#endif
+
 #if HAS_GYRO
 #include <ao_quaternion.h>
 #endif
@@ -57,7 +61,7 @@ angle_t               ao_sample_orient;
 angle_t                ao_sample_orients[AO_NUM_ORIENT];
 uint8_t                ao_sample_orient_pos;
 #endif
-#ifdef HAS_MOTOR_PRESSURE
+#if HAS_MOTOR_PRESSURE
 motor_pressure_t       ao_sample_motor_pressure;
 #endif
 
@@ -340,7 +344,7 @@ ao_sample_preflight_update(void)
                ++nsamples;
        else
                ao_sample_preflight_set();
-#if !HAS_BARO
+#if !HAS_BARO && HAS_KALMAN
        if ((nsamples & 0x3f) == 0)
                ao_kalman_reset_accumulate();
 #endif
@@ -406,7 +410,9 @@ ao_sample(void)
                else {
                        if (ao_flight_state < ao_flight_boost)
                                ao_sample_preflight_update();
+#if HAS_KALMAN
                        ao_kalman();
+#endif
 #if HAS_GYRO
                        ao_sample_rotate();
 #endif
index dd278bde064740f3ed0ac1a6ef11d90ccb85ab7c..b50454167b67d70fdcea806f75ac3a21afb74b9d 100644 (file)
@@ -57,7 +57,17 @@ static inline void ao_check_stack(void) {
 #endif
 
 #ifndef SLEEP_HASH_SIZE
+#ifdef __ARM_FEATURE_IDIV__
 #define SLEEP_HASH_SIZE        17
+#else
+#define SLEEP_HASH_SIZE 16
+#endif
+#endif
+
+#if SLEEP_HASH_SIZE & (SLEEP_HASH_SIZE - 1)
+#define SLEEP_HASH_SHIFT 0
+#else
+#define SLEEP_HASH_SHIFT 2
 #endif
 
 static struct ao_list  run_queue;
@@ -75,7 +85,7 @@ _ao_task_to_run_queue(struct ao_task *task)
 static struct ao_list *
 ao_task_sleep_queue(void *wchan)
 {
-       return &ao_sleep_queue[(uintptr_t) wchan % SLEEP_HASH_SIZE];
+       return &ao_sleep_queue[(((uintptr_t) wchan) >> SLEEP_HASH_SHIFT) % SLEEP_HASH_SIZE];
 }
 
 static void
index 1fd4037f540b8d0d01618b6a22672cd613bb187b..8586efd548f59cce09f4aa4b0de43a439471ba5b 100644 (file)
@@ -58,7 +58,7 @@ static AO_TICK_TYPE ao_aprs_time;
 #define AO_SEND_MEGA   1
 #endif
 
-#if defined (TELEMETRUM_V_2_0) || defined (TELEMETRUM_V_3_0)
+#if defined (TELEMETRUM_V_2_0) || defined (TELEMETRUM_V_3_0) || defined (TELEMETRUM_V_4_0)
 #define AO_SEND_METRUM 1
 #endif
 
@@ -142,6 +142,8 @@ ao_send_mega_sensor(void)
 #if AO_LOG_NORMALIZED
 #if AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_5
        telemetry.generic.type = AO_TELEMETRY_MEGA_NORM_MPU6000_MMC5983;
+#elif AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_6
+       telemetry.generic.type = AO_TELEMETRY_MEGA_NORM_BMI088_MMC5983;
 #else
 #error unknown normalized log type
 #endif
@@ -153,7 +155,7 @@ ao_send_mega_sensor(void)
        telemetry.mega_norm.pres = ao_data_pres(packet);
        telemetry.mega_norm.temp = ao_data_temp(packet);
 
-#if HAS_MPU6000
+#ifdef ao_data_along
        telemetry.mega_norm.accel_along = ao_data_along(packet);
        telemetry.mega_norm.accel_across = ao_data_across(packet);
        telemetry.mega_norm.accel_through = ao_data_through(packet);
@@ -163,7 +165,7 @@ ao_send_mega_sensor(void)
        telemetry.mega_norm.gyro_yaw = ao_data_yaw(packet);
 #endif
 
-#if HAS_MMC5983
+#ifdef ao_data_mag_along
        telemetry.mega_norm.mag_along = ao_data_mag_along(packet);
        telemetry.mega_norm.mag_across = ao_data_mag_across(packet);
        telemetry.mega_norm.mag_through = ao_data_mag_through(packet);
@@ -490,7 +492,10 @@ ao_set_aprs_time(void)
                } else {
                        delta = second - ao_gps_data.second;
                }
-               ao_aprs_time = ao_gps_tick + AO_SEC_TO_TICKS(delta);
+               if (delta < (interval >> 1))
+                       delta += interval;
+
+               ao_aprs_time = ao_gps_utc_tick + AO_SEC_TO_TICKS(delta);
        } else {
                ao_aprs_time += AO_SEC_TO_TICKS(ao_config.aprs_interval);
        }
index 251a3fcda3fd36934d4e336617cbb20ef267fed9..983a99b8fd1cba7ef4adf0b4d41a047f9bb5f1e8 100644 (file)
@@ -298,6 +298,7 @@ struct ao_telemetry_mini {
 };
 
 #define AO_TELEMETRY_MEGA_NORM_MPU6000_MMC5983 0x13
+#define AO_TELEMETRY_MEGA_NORM_BMI088_MMC5983  0x14
 
 struct ao_telemetry_mega_norm {
        uint16_t        serial;         /*  0 */
diff --git a/src/metro-m0/ao_pins.h b/src/metro-m0/ao_pins.h
new file mode 100644 (file)
index 0000000..0f276c1
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define LED_0_PORT     (&samd21_port_a)
+#define LED_0_PIN      2
+
+#define LED_BLUE       (1 << 0)
+
+#define AO_LED_PANIC   LED_BLUE
+
+#define HAS_USB                1
+#define USE_USB_STDIO  1
+
+#define HAS_LED                1
+
+#define AO_DFLL48M             48000000
+#define AO_XOSC32K             32768
+
+#define AO_AHB_PRESCALER       1
+#define AO_APBA_PRESCALER      1
+
+#define HAS_SPI_0              1
+#define HAS_SPI_5              1
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS          1
+
+#if 1
+#define AO_M25_SPI_CS_PORT     (&samd21_port_a)
+#define AO_M25_SPI_CS_MASK     (1 << 13)
+#define AO_M25_SPI_BUS         AO_SPI_5_PB22_PB23_PB03
+#else
+
+#define AO_M25_SPI_CS_PORT     (&samd21_port_a)
+#define AO_M25_SPI_CS_MASK     (1 << 14) /* D2 */
+#define AO_M25_SPI_BUS         AO_SPI_4_PB10_PB11_PA12
+
+#endif
+
+/*
+ * Beeper
+ */
+
+#define HAS_BEEP               1
+
+/* Beep on PA11 function F TCC0.3 */
+
+#define AO_BEEP_TCC    (&samd21_tcc0)
+#define AO_BEEP_TCC_APBC_MASK  SAMD21_PM_APBCMASK_TCC0
+#define AO_BEEP_PORT   (&samd21_port_a)
+#define AO_BEEP_PIN    (11)
+#define AO_BEEP_FUNC   SAMD21_PORT_PMUX_FUNC_F
+
+/* ADC */
+#define AO_DATA_RING           32
+
+#define HAS_ADC                        1
+
+struct ao_adc {
+       int16_t                 a[6];
+       int16_t                 temp;
+};
+
+#define AO_NUM_ADC_PIN 6
+#define AO_NUM_ADC     (AO_NUM_ADC_PIN + 1)
+
+#define AO_ADC_DUMP(p)                                                 \
+       printf("tick: %5lu a0: %5d a1: %5d a2: %5d a3: %5d a4: %5d a5: %5d temp: %5d\n", \
+              (p)->tick,                                               \
+              (p)->adc.a[0], (p)->adc.a[1], (p)->adc.a[2],             \
+              (p)->adc.a[3], (p)->adc.a[4], (p)->adc.a[5],             \
+              (p)->adc.temp);
+
+#define AO_ADC_PIN0_PORT       (&samd21_port_a)
+#define AO_ADC_PIN0_PIN                2
+#define AO_ADC_SQ0             0
+
+#define AO_ADC_PIN1_PORT       (&samd21_port_b)
+#define AO_ADC_PIN1_PIN                8
+#define AO_ADC_SQ1             2
+
+#define AO_ADC_PIN2_PORT       (&samd21_port_b)
+#define AO_ADC_PIN2_PIN                9
+#define AO_ADC_SQ2             3
+
+#define AO_ADC_PIN3_PORT       (&samd21_port_a)
+#define AO_ADC_PIN3_PIN                4
+#define AO_ADC_SQ3             4
+
+#define AO_ADC_PIN4_PORT       (&samd21_port_a)
+#define AO_ADC_PIN4_PIN                5
+#define AO_ADC_SQ4             5
+
+#define AO_ADC_PIN5_PORT       (&samd21_port_b)
+#define AO_ADC_PIN5_PIN                2
+#define AO_ADC_SQ5             10
+
+#define AO_ADC_SQ6             SAMD21_ADC_INPUTCTRL_MUXPOS_TEMP
+
+/* GPS */
+#define HAS_GPS                        1
+
+#define AO_SERIAL_SPEED_UBLOX  AO_SERIAL_SPEED_9600
+
+#define HAS_SERIAL_0           1
+#define USE_SERIAL_0_STDIN     0
+#define SERIAL_0_PA08_PA09     1
+
+#define ao_gps_getchar         ao_serial0_getchar
+#define ao_gps_putchar         ao_serial0_putchar
+#define ao_gps_set_speed       ao_serial0_set_speed
+#define ao_gps_fifo            (ao_samd21_usart0.rx_fifo)
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/metro-m0/flash-loader/ao_pins.h b/src/metro-m0/flash-loader/ao_pins.h
new file mode 100644 (file)
index 0000000..9a62148
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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_
+
+#include <ao_flash_samd21_pins.h>
+
+/* A0 to gnd for boot loader mode */
+
+#define AO_BOOT_PIN                    1
+#define AO_BOOT_APPLICATION_GPIO       (samd21_port_a)
+#define AO_BOOT_APPLICATION_PIN                2
+#define AO_BOOT_APPLICATION_VALUE      1
+#define AO_BOOT_APPLICATION_MODE       AO_MODE_PULL_UP
+
+/* USB */
+#define HAS_USB                        1
+
+#define AO_DFLL48M             48000000
+#define AO_XOSC32K             32768
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/metro-m0/metro-m0.c b/src/metro-m0/metro-m0.c
new file mode 100644 (file)
index 0000000..5fe7723
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#include <ao.h>
+#include <ao_led.h>
+#include <ao_dma_samd21.h>
+#include <ao_exti.h>
+
+static int     pressed;
+
+static void
+ao_button_callback(void)
+{
+       pressed = 1;
+       ao_wakeup(&pressed);
+}
+
+static void
+ao_beep_test(void)
+{
+       AO_BEEP_TCC->ctrlbset = (SAMD21_TCC_CTRLB_CMD_READSYNC << SAMD21_TCC_CTRLB_CMD);
+       printf("pressed timer %ld\n", AO_BEEP_TCC->count);
+       fflush(stdout);
+       ao_beep_for(AO_BEEP_MID_DEFAULT, AO_MS_TO_TICKS(200));
+}
+
+static void
+ao_button(void)
+{
+       ao_exti_setup(&samd21_port_a, 10, AO_EXTI_MODE_FALLING | AO_EXTI_MODE_PULL_UP, ao_button_callback);
+       ao_exti_enable(&samd21_port_a, 10);
+       for (;;) {
+               ao_arch_block_interrupts();
+               pressed = 0;
+               while (!pressed)
+                       ao_sleep(&pressed);
+               ao_arch_release_interrupts();
+               ao_beep_test();
+       }
+}
+
+static struct ao_task ao_button_task;
+
+const struct ao_cmds ao_test_cmds[] = {
+       { ao_beep_test, "b \0beep" },
+       { 0, NULL },
+};
+
+int main(void)
+{
+       ao_led_init();
+       ao_clock_init();
+       ao_task_init();
+       ao_timer_init();
+       ao_dma_init();
+       ao_exti_init();
+       ao_spi_init();
+       ao_adc_init();
+       ao_serial_init();
+       ao_gps_init();
+       ao_beep_init();
+       ao_usb_init();
+       ao_storage_init();
+       ao_cmd_init();
+
+       ao_cmd_register(ao_test_cmds);
+       ao_add_task(&ao_button_task, ao_button, "button");
+       ao_start_scheduler();
+
+       return 0;
+}
index fba99c88b6931a4166eaa66c54d8ff5fbcec4f6c..538b98a002b7262e488ee1eddaef7e1ac99e4f65 100644 (file)
@@ -3,7 +3,9 @@
 #
 #
 
-include ../stmf0/Makefile.defs
+TOPDIR=..
+
+include $(TOPDIR)/stmf0/Makefile.defs
 
 INC = \
        ao.h \
@@ -13,8 +15,6 @@ INC = \
        ao_pins.h \
        ao_product.h \
        ao_task.h \
-       ao_lisp.h \
-       ao_lisp_const.h \
        stm32f0.h \
        Makefile
 
@@ -25,42 +25,24 @@ ALTOS_SRC = \
        ao_romconfig.c \
        ao_cmd.c \
        ao_config.c \
+       ao_data.c \
        ao_task.c \
        ao_led_stmf0.c \
-       ao_beep_stm.c \
        ao_dma_stm.c \
        ao_stdio.c \
        ao_panic.c \
        ao_timer.c \
        ao_mutex.c \
        ao_usb_stm.c \
-       ao_serial_stm.c \
        ao_flash_stm.c \
-       ao_lisp_atom.c \
-       ao_lisp_builtin.c \
-       ao_lisp_cons.c \
-       ao_lisp_error.c \
-       ao_lisp_eval.c \
-       ao_lisp_frame.c \
-       ao_lisp_int.c \
-       ao_lisp_lambda.c \
-       ao_lisp_lex.c \
-       ao_lisp_mem.c \
-       ao_lisp_poly.c \
-       ao_lisp_read.c \
-       ao_lisp_rep.c \
-       ao_lisp_save.c \
-       ao_lisp_stack.c \
-       ao_lisp_string.c \
-       ao_lisp_os_save.c
+       ao_spi_stm.c \
+       ao_bmi088.c
 
 PRODUCT=Nucleo-32
 PRODUCT_DEF=-DNUCLEO
 IDPRODUCT=0x000a
 
-CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS)
-
-LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stmf0 -Wl,-Tload.ld -n
+CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS) $(PROFILE_DEF)
 
 PROGNAME=nucleo-32
 PROG=$(PROGNAME)-$(VERSION).elf
@@ -72,20 +54,14 @@ OBJ=$(SRC:.c=.o)
 all: $(PROG) $(HEX)
 
 $(PROG): Makefile $(OBJ) altos.ld
-       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+       $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS) -Wl,-Map=$(PROGNAME)-$(VERSION).map
 
 $(OBJ): $(INC)
 
-ao_product.h: ao-make-product.5c ../Version
-       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
-
-load: $(PROG)
-       stm-load $(PROG)
-
 distclean:     clean
 
 clean:
-       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
        rm -f ao_product.h
 
 install:
index 6b4cbaaed195d23687c6f670951fd35e96a0b53e..5854fe58860d8fa76525ce119967805d0d4a0117 100644 (file)
  */
 
 #include <ao.h>
-#include <ao_lisp.h>
-#include <ao_beep.h>
+#include <ao_bmi088.h>
 
-static void lisp_cmd() {
-       ao_lisp_read_eval_print();
-}
-
-static void beep() {
-       ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
-}
-
-static const struct ao_cmds blink_cmds[] = {
-       { lisp_cmd,     "l\0Run lisp interpreter" },
-       { beep,         "b\0Beep" },
-       { 0, 0 }
-};
+uint8_t        ao_sensor_errors;
 
-void main(void)
+int main(void)
 {
-       ao_led_init(LEDS_AVAILABLE);
+       ao_led_init();
        ao_clock_init();
        ao_task_init();
        ao_timer_init();
        ao_dma_init();
        ao_usb_init();
-       ao_serial_init();
-       ao_beep_init();
+       ao_spi_init();
+       ao_bmi088_init();
        ao_cmd_init();
-       ao_cmd_register(blink_cmds);
        ao_start_scheduler();
 }
 
index cee4616f08ceabd872beace2dcbd7782f8ae8665..0cc91d7334f5e0dcf91c89a0b8862fc6710dddf2 100644 (file)
 #define HAS_USB                                1
 #define AO_USB_DIRECTIO                        0
 #define AO_PA11_PA12_RMP               0
-#define HAS_BEEP                       1
-
-#define BEEPER_TIMER                   2
-#define BEEPER_CHANNEL                 4
-#define BEEPER_PORT                    (&stm_gpioa)
-#define BEEPER_PIN                     3
+#define HAS_BEEP                       0
 
 #define IS_FLASH_LOADER        0
 
-#define HAS_SERIAL_2           1
+#define HAS_SERIAL_2           0
 #define SERIAL_2_PA2_PA15      1
 #define USE_SERIAL_2_FLOW      0
 #define USE_SERIAL_2_STDIN     1
 #define DELAY_SERIAL_2_STDIN   0
 
+#define HAS_SPI_1      1
+#define SPI_1_PA5_PA6_PA7      1
+#define SPI_1_OSPEEDR          STM_OSPEEDR_HIGH
+#define SPI_1_PB3_PB4_PB5      0
+
+#define HAS_SPI_2      0
+
+#define HAS_BMI088     1
+#define HAS_IMU                1
+
+#define ao_data_along(packet)   ((packet)->bmi088.acc.x)
+#define ao_data_across(packet)  (-(packet)->bmi088.acc.y)
+#define ao_data_through(packet) ((packet)->bmi088.acc.z)
+
+#define ao_data_roll(packet)    ((packet)->bmi088.gyr.x)
+#define ao_data_pitch(packet)   (-(packet)->bmi088.gyr.y)
+#define ao_data_yaw(packet)     ((packet)->bmi088.gyr.z)
+
+#define AO_BMI088_ACC_CS_PORT  (&stm_gpioa)
+#define AO_BMI088_ACC_CS_PIN   0
+#define AO_BMI088_GYR_CS_PORT  (&stm_gpioa)
+#define AO_BMI088_GYR_CS_PIN   1
+#define AO_BMI088_SPI_BUS      (AO_SPI_1_PA5_PA6_PA7 | AO_SPI_MODE_0)
+
+#define AO_DATA_RING   32
+
 #endif /* _AO_PINS_H_ */
diff --git a/src/samd21/Makefile-flash.defs b/src/samd21/Makefile-flash.defs
new file mode 100644 (file)
index 0000000..d489bf1
--- /dev/null
@@ -0,0 +1,58 @@
+include $(TOPDIR)/samd21/Makefile-samd21.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_flash_pins.h \
+       ao_flash_samd21_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_timer.c \
+       ao_usb_samd21.c \
+       ao_flash_samd21.c \
+       ao_flash_task.c \
+       ao_flash_loader_samd21.c
+
+OBJ=$(SRC:.c=.o)
+
+PRODUCT=AltosFlash
+PRODUCT_DEF=-DALTOS_FLASH
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(SAMD21_CFLAGS)
+
+LDFLAGS=-nostartfiles $(CFLAGS) -L$(TOPDIR)/samd21 -Taltos-loader.ld -n
+
+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:
diff --git a/src/samd21/Makefile-samd21.defs b/src/samd21/Makefile-samd21.defs
new file mode 100644 (file)
index 0000000..ef8713c
--- /dev/null
@@ -0,0 +1,12 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/Makefile.defs
+
+vpath % $(TOPDIR)/samd21:$(AO_VPATH)
+
+CC=$(ARM_CC)
+
+SAMD21_CFLAGS=-mlittle-endian -mcpu=cortex-m0 -mthumb \
+       -I$(TOPDIR)/samd21 $(AO_CFLAGS) $(PICOLIBC_CFLAGS)
diff --git a/src/samd21/Makefile.defs b/src/samd21/Makefile.defs
new file mode 100644 (file)
index 0000000..d58db93
--- /dev/null
@@ -0,0 +1,11 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/samd21/Makefile-samd21.defs
+
+SAMD21_LINKER_SCRIPT=altos-$(SAMD21_ROM)-$(SAMD21_RAM).ld 
+
+LDFLAGS=-nostartfiles $(CFLAGS) -L$(TOPDIR)/samd21 -T$(SAMD21_LINKER_SCRIPT) -n
+
+.DEFAULT_GOAL=all
diff --git a/src/samd21/altos-128-16.ld b/src/samd21/altos-128-16.ld
new file mode 100644 (file)
index 0000000..e07a6a4
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+__flash = 0x00001000;
+__flash_size = 128K - 4K;
+__ram = 0x20000000;
+__ram_size = 16k;
+__stack_size = 256;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
diff --git a/src/samd21/altos-256-32.ld b/src/samd21/altos-256-32.ld
new file mode 100644 (file)
index 0000000..4be7d67
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+__flash = 0x00001000;
+__flash_size = 256 - 4K;
+__ram = 0x20000000;
+__ram_size = 32k;
+__stack_size = 256;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
diff --git a/src/samd21/altos-loader.ld b/src/samd21/altos-loader.ld
new file mode 100644 (file)
index 0000000..a06d28b
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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 = 0x00000000;
+__flash_size = 4K;
+__ram = 0x20000000;
+__ram_size = 4k;
+__stack_size = 256;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
diff --git a/src/samd21/ao_adc_samd21.c b/src/samd21/ao_adc_samd21.c
new file mode 100644 (file)
index 0000000..6d67eda
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright © 2019 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.
+ */
+
+#include <ao.h>
+#include <ao_adc_samd21.h>
+
+static void
+ao_adc_sync(void)
+{
+       while (samd21_adc.status & (1 << SAMD21_ADC_STATUS_SYNCBUSY))
+               ;
+}
+
+static uint8_t ao_adc_sequence;
+static uint8_t ao_adc_ready;
+
+static uint8_t ao_adc_mux[AO_NUM_ADC] = {
+#if AO_NUM_ADC > 0
+       AO_ADC_SQ0,
+#endif
+#if AO_NUM_ADC > 1
+       AO_ADC_SQ1,
+#endif
+#if AO_NUM_ADC > 2
+       AO_ADC_SQ2,
+#endif
+#if AO_NUM_ADC > 3
+       AO_ADC_SQ3,
+#endif
+#if AO_NUM_ADC > 4
+       AO_ADC_SQ4,
+#endif
+#if AO_NUM_ADC > 5
+       AO_ADC_SQ5,
+#endif
+#if AO_NUM_ADC > 6
+       AO_ADC_SQ6,
+#endif
+#if AO_NUM_ADC > 7
+       AO_ADC_SQ7,
+#endif
+#if AO_NUM_ADC > 8
+       AO_ADC_SQ8,
+#endif
+#if AO_NUM_ADC > 9
+#error set up more ADC
+#endif
+};
+
+static void
+ao_adc_start(void)
+{
+       uint8_t mux = ao_adc_mux[ao_adc_sequence];
+       samd21_adc.inputctrl = ((mux << SAMD21_ADC_INPUTCTRL_MUXPOS) |
+                               (SAMD21_ADC_INPUTCTRL_MUXNEG_GND << SAMD21_ADC_INPUTCTRL_MUXNEG) |
+                               (0 << SAMD21_ADC_INPUTCTRL_INPUTSCAN) |
+                               (0 << SAMD21_ADC_INPUTCTRL_INPUTOFFSET) |
+                               (SAMD21_ADC_INPUTCTRL_GAIN_DIV2 << SAMD21_ADC_INPUTCTRL_GAIN));
+       samd21_adc.swtrig = (1UL << SAMD21_ADC_SWTRIG_START);
+}
+
+void
+samd21_adc_isr(void)
+{
+       uint16_t        *out;
+
+       /* Store converted value in packet */
+       out = (uint16_t *) &ao_data_ring[ao_data_head].adc;
+       out[ao_adc_sequence] = (uint16_t) samd21_adc.result;
+       if (++ao_adc_sequence < AO_NUM_ADC) {
+               ao_adc_start();
+               return;
+       }
+
+       AO_DATA_PRESENT(AO_DATA_ADC);
+       ao_data_fill(ao_data_head);
+       ao_adc_ready = 1;
+}
+
+void
+ao_adc_poll(void)
+{
+       if (!ao_adc_ready)
+               return;
+       ao_adc_ready = 0;
+       ao_adc_sequence = 0;
+       ao_adc_start();
+}
+
+static void
+ao_adc_dump(void)
+{
+       struct ao_data  packet;
+
+       ao_data_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
+set_adc(struct samd21_port *port, uint8_t pin)
+{
+       samd21_port_pmux_set(port, pin, SAMD21_PORT_PMUX_FUNC_B);
+       samd21_port_pincfg_set(port, pin,
+                              (1 << SAMD21_PORT_PINCFG_DRVSTR) |
+                              (1 << SAMD21_PORT_PINCFG_PULLEN) |
+                              (1 << SAMD21_PORT_PINCFG_INEN),
+                              (0 << SAMD21_PORT_PINCFG_DRVSTR) |
+                              (0 << SAMD21_PORT_PINCFG_PULLEN) |
+                              (1 << SAMD21_PORT_PINCFG_INEN));
+}
+
+void
+ao_adc_init(void)
+{
+       /* supply a clock */
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_ADC);
+
+       /* enable the device */
+       samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_ADC);
+
+       /* Reset */
+       samd21_adc.ctrla = (1 << SAMD21_ADC_CTRLA_SWRST);
+
+       ao_adc_sync();
+
+       while ((samd21_adc.ctrla & (1 << SAMD21_ADC_CTRLA_SWRST)) != 0 ||
+              (samd21_adc.status & (1 << SAMD21_ADC_STATUS_SYNCBUSY)) != 0)
+               ao_adc_sync();
+
+       /* Load ADC calibration values */
+       uint32_t b = (samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_ADC_BIASCAL) & SAMD21_AUX1_CALIBRATION_ADC_BIASCAL_MASK;
+       uint32_t l = (samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_ADC_LINEARITY) & SAMD21_AUX1_CALIBRATION_ADC_LINEARITY_MASK;
+
+       samd21_adc.calib = (uint16_t) ((b << SAMD21_ADC_CALIB_BIAS_CAL) |
+                                      (l << SAMD21_ADC_CALIB_LINEARITY_CAL));
+
+
+       ao_adc_sync();
+
+       samd21_adc.ctrlb = ((0 << SAMD21_ADC_CTRLB_DIFFMODE) |
+                           (0 << SAMD21_ADC_CTRLB_LEFTADJ) |
+                           (0 << SAMD21_ADC_CTRLB_FREERUN) |
+                           (0 << SAMD21_ADC_CTRLB_CORREN) |
+                           (SAMD21_ADC_CTRLB_RESSEL_12BIT << SAMD21_ADC_CTRLB_RESSEL) |
+                           (SAMD21_ADC_CTRLB_PRESCALER_DIV512 << SAMD21_ADC_CTRLB_PRESCALER));
+
+       ao_adc_sync();
+
+       samd21_adc.sampctrl = 0x1f;
+
+       ao_adc_sync();
+
+       samd21_adc.refctrl = (SAMD21_ADC_REFCTRL_REFSEL_INTVCC1 << SAMD21_ADC_REFCTRL_REFSEL);
+
+       ao_adc_sync();
+
+       samd21_adc.intenset = (1UL << SAMD21_ADC_INTFLAG_RESRDY);
+
+       samd21_adc.ctrla = (1 << SAMD21_ADC_CTRLA_ENABLE);
+
+       /* configure interrupts */
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_ADC_POS);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_ADC_POS, 0);
+
+       ao_cmd_register(&ao_adc_cmds[0]);
+
+       /* configure pins */
+#if AO_NUM_ADC_PIN > 0
+       set_adc(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 1
+       set_adc(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 2
+       set_adc(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 3
+       set_adc(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 4
+       set_adc(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 5
+       set_adc(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 6
+       set_adc(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 7
+       set_adc(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 8
+       set_adc(AO_ADC_PIN8_PORT, AO_ADC_PIN8_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 9
+#error set up more ADC bits
+#endif
+
+       ao_adc_ready = 1;
+}
diff --git a/src/samd21/ao_adc_samd21.h b/src/samd21/ao_adc_samd21.h
new file mode 100644 (file)
index 0000000..3d1658b
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2019 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_SAMD21_H_
+#define _AO_ADC_SAMD21_H_
+
+void
+ao_adc_poll(void);
+
+void
+ao_adc_init(void);
+
+#endif /* _AO_ADC_SAMD21_H_ */
diff --git a/src/samd21/ao_apa102.c b/src/samd21/ao_apa102.c
new file mode 100644 (file)
index 0000000..e391f96
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2019 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <ao.h>
+#include <ao_snek.h>
+
+#define bit(v) do {                            \
+               port_c->outtgl = (1 << pin_c);  \
+               ao_arch_nop(); \
+               ao_gpio_set(port_d, pin_d, v);  \
+               ao_arch_nop(); \
+               port_c->outtgl = (1 << pin_c);  \
+               ao_arch_nop(); \
+       } while (0)
+
+#define byte(v)        do {                            \
+               uint8_t _bit_ = 0x80;           \
+               while (_bit_) {                 \
+                       bit(!!((v) & _bit_));   \
+                       _bit_ >>= 1;            \
+               }                               \
+       } while(0)
+
+#define repeat(v,c) do {                       \
+               uint8_t _i_;                    \
+               for (_i_ = 0; _i_ < (c); _i_++) \
+                       bit(v);                 \
+       } while (0)
+
+void
+ao_snek_apa102_write(void *gpio_d, uint8_t pin_d,
+                    void *gpio_c, uint8_t pin_c,
+                    int npixel,
+                    struct snek_neopixel *pixels)
+{
+       struct samd21_port *port_d = gpio_d;
+       struct samd21_port *port_c = gpio_c;
+
+       ao_gpio_set(port_c, pin_c, 1);
+       int i;
+       for (i = 0; i < 32; i++)
+               ao_arch_nop();
+       repeat(0, 32);
+       while (npixel--) {
+               byte(0xff);
+               byte(pixels->b);
+               byte(pixels->g);
+               byte(pixels->r);
+       }
+       repeat(1, 32);
+}
diff --git a/src/samd21/ao_arch.h b/src/samd21/ao_arch.h
new file mode 100644 (file)
index 0000000..a5f68da
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright © 2019 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 <stdio.h>
+#include <samd21.h>
+
+/*
+ * Samd21 definitions and code fragments for AltOS
+ */
+
+#define AO_PORT_TYPE   uint32_t
+
+#define AO_LED_TYPE    AO_PORT_TYPE
+
+#define ao_arch_naked_declare  __attribute__((naked))
+#define ao_arch_naked_define
+
+#define ao_arch_reboot() \
+       (samd21_scb.aircr = ((SAMD21_SCB_AIRCR_VECTKEY_KEY << SAMD21_SCB_AIRCR_VECTKEY) | \
+                         (1 << SAMD21_SCB_AIRCR_SYSRESETREQ)))
+
+#define ao_arch_nop()          asm("nop")
+#define ao_arch_interrupt(n)   /* nothing */
+#define ao_arch_block_interrupts()     asm("cpsid i")
+#define ao_arch_release_interrupts()   asm("cpsie i")
+
+/* ao_romconfig.c */
+#define AO_ROMCONFIG_SYMBOL __attribute__((section(".init.1"))) const
+#define AO_USBCONFIG_SYMBOL __attribute__((section(".init.2"))) const
+
+/*
+ * ao_timer.c
+ *
+ * For the samd21, we want to use the DFLL48 clock
+ */
+
+/* GCLK 0 is always the system clock source */
+
+#define AO_GCLK_SYSCLK 0
+
+/* If there's a 32kHz xtal, use that for the 32kHz oscillator */
+#ifdef AO_XOSC32K
+# ifndef AO_GCLK_XOSC32K
+#  define AO_GCLK_XOSC32K      1
+# endif
+#endif
+
+/* If there's a high-freq xtal, use that */
+#ifdef AO_XOSC
+# ifndef AO_GCLK_XOSC
+#  define AO_GCLK_XOSC 1
+# endif
+
+# ifndef AO_XOSC_GCLK_DIV
+#  define AO_XOSC_GCLK_DIV 1
+# endif
+
+# define AO_FDPLL96M   ((AO_XOSC_FREQ / AO_XOSC_DIV * AO_XOSC_MUL) / AO_XOSC_GCLK_DIV)
+
+/* By default, use the xosc for the system, but allow the dfll48m to
+ * drive USB if desired.
+ */
+
+# ifndef AO_GCLK_FDPLL96M
+#  define AO_SYSCLK            AO_FDPLL96M
+#  define AO_GCLK_FDPLL96M     AO_GCLK_SYSCLK
+#  define AO_GCLK_DFLL48M      2
+# endif
+
+#endif /* AO_XOSC */
+
+#if AO_DFLL48M
+# ifndef AO_GCLK_DFLL48M
+#  define AO_SYSCLK    AO_DFLL48M
+#  define AO_GCLK_DFLL48M AO_GCLK_SYSCLK
+# endif
+#endif
+
+#ifndef AO_GCLK_USB
+# if AO_DFLL48M
+#  define AO_GCLK_USB AO_GCLK_DFLL48M
+# else
+#  define AO_GCLK_USB AO_GCLK_SYSCLK
+# endif
+#endif
+
+/*
+ * ao_timer.c
+ *
+ * For the samd21, we want to use the DFLL48 clock
+ */
+
+/* GCLK 0 is always the system clock source */
+
+#define AO_GCLK_SYSCLK 0
+
+/* If there's a 32kHz xtal, use that for the 32kHz oscillator */
+#ifdef AO_XOSC32K
+# ifndef AO_GCLK_XOSC32K
+#  define AO_GCLK_XOSC32K      1
+# endif
+#endif
+
+/* If there's a high-freq xtal, use that */
+#ifdef AO_XOSC
+# ifndef AO_GCLK_XOSC
+#  define AO_GCLK_XOSC 1
+# endif
+
+# ifndef AO_XOSC_GCLK_DIV
+#  define AO_XOSC_GCLK_DIV 1
+# endif
+
+# define AO_FDPLL96M   ((AO_XOSC_FREQ / AO_XOSC_DIV * AO_XOSC_MUL) / AO_XOSC_GCLK_DIV)
+
+/* By default, use the xosc for the system, but allow the dfll48m to
+ * drive USB if desired.
+ */
+
+# ifndef AO_GCLK_FDPLL96M
+#  define AO_SYSCLK            AO_FDPLL96M
+#  define AO_GCLK_FDPLL96M     AO_GCLK_SYSCLK
+#  define AO_GCLK_DFLL48M      2
+# endif
+
+#endif /* AO_XOSC */
+
+#if AO_DFLL48M
+# ifndef AO_GCLK_DFLL48M
+#  define AO_SYSCLK    AO_DFLL48M
+#  define AO_GCLK_DFLL48M AO_GCLK_SYSCLK
+# endif
+#endif
+
+#ifndef AO_GCLK_USB
+# if AO_DFLL48M
+#  define AO_GCLK_USB AO_GCLK_DFLL48M
+# else
+#  define AO_GCLK_USB AO_GCLK_SYSCLK
+# endif
+#endif
+
+#define AO_HCLK                (AO_SYSCLK / AO_AHB_PRESCALER)
+
+#define AO_HCLK                (AO_SYSCLK / AO_AHB_PRESCALER)
+#define AO_PCLK                (AO_HCLK / AO_APB_PRESCALER)
+#define AO_SYSTICK     (AO_HCLK)
+#define AO_PANIC_DELAY_SCALE  (AO_SYSCLK / 12000000)
+
+#define AO_SAMD21_NVIC_HIGH_PRIORITY   (0 << 6)
+#define AO_SAMD21_NVIC_CLOCK_PRIORITY  (1 << 6)
+#define AO_SAMD21_NVIC_MED_PRIORITY    (2 << 6)
+#define AO_SAMD21_NVIC_LOW_PRIORITY    (3 << 6)
+
+/* ADC maximum reported value */
+#define AO_ADC_MAX                     4095
+
+#define AO_SAMD21_NVIC_HIGH_PRIORITY   (0 << 6)
+#define AO_SAMD21_NVIC_CLOCK_PRIORITY  (1 << 6)
+#define AO_SAMD21_NVIC_MED_PRIORITY    (2 << 6)
+#define AO_SAMD21_NVIC_LOW_PRIORITY    (3 << 6)
+
+/* ADC maximum reported value */
+#define AO_ADC_MAX                     4095
+
+/* This has to be 65536 so that TCC and TC match; TC isn't configurable */
+#define AO_TCC_PERIOD          65536
+#define SNEK_PWM_MAX           (AO_TCC_PERIOD-1)
+
+#define AO_TICK_TYPE           uint32_t
+#define AO_TICK_SIGNED         int32_t
+
+bool
+ao_usb_waiting(void);
+
+#define AO_CMD_LEN             128
+
+#ifndef AO_STACK_SIZE
+#define AO_STACK_SIZE          512
+#endif
+
+#ifndef HAS_BOOT_LOADER
+#define HAS_BOOT_LOADER                        1
+#endif
+
+#if HAS_BOOT_LOADER
+#define AO_BOOT_APPLICATION_BASE       ((uint32_t *) 0x00001000)
+#ifndef AO_BOOT_APPLICATION_BOUND
+#define AO_BOOT_APPLICATION_BOUND      ((uint32_t *) (0x00000000 + samd21_flash_size()))
+#endif
+#define AO_BOOT_LOADER_BASE            ((uint32_t *) 0x00000000)
+#endif
+
+#endif /* _AO_ARCH_H_ */
diff --git a/src/samd21/ao_arch_funcs.h b/src/samd21/ao_arch_funcs.h
new file mode 100644 (file)
index 0000000..32670b4
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * Copyright © 2019 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_MODE_PULL_NONE      0
+#define AO_MODE_PULL_UP                1
+#define AO_MODE_PULL_DOWN      2
+
+static inline void ao_enable_port(struct samd21_port *port)
+{
+       (void) port;
+       samd21_pm.apbbmask |= (1UL << SAMD21_PM_APBBMASK_PORT);
+}
+
+static inline void ao_disable_port(struct samd21_port *port)
+{
+       (void) port;
+       samd21_pm.apbbmask &= ~(1UL << SAMD21_PM_APBBMASK_PORT);
+}
+
+static inline void
+ao_gpio_set(struct samd21_port *port, uint8_t bit, uint8_t v)
+{
+       if (v)
+               port->outset = (1 << bit);
+       else
+               port->outclr = (1 << bit);
+}
+
+static inline uint8_t
+ao_gpio_get(struct samd21_port *port, uint8_t bit)
+{
+       return (port->in >> bit) & 1;
+}
+
+static inline void
+ao_gpio_dir_set(struct samd21_port *port, uint8_t bit, bool output)
+{
+       if (output)
+               port->dirset = (1 << bit);
+       else
+               port->dirclr = (1 << bit);
+}
+
+static inline void
+ao_gpio_set_mode(struct samd21_port *port, uint8_t bit, uint32_t mode)
+{
+       uint8_t pincfg = 0;
+
+       if (mode != AO_MODE_PULL_NONE) {
+               pincfg |= (1 << SAMD21_PORT_PINCFG_PULLEN);
+               ao_gpio_set(port, bit, mode == AO_MODE_PULL_UP);
+       }
+
+       samd21_port_pincfg_set(port, bit,
+                              (0 << SAMD21_PORT_PINCFG_DRVSTR) |
+                              (1 << SAMD21_PORT_PINCFG_PULLEN) |
+                              (0 << SAMD21_PORT_PINCFG_INEN) |
+                              (0 << SAMD21_PORT_PINCFG_PMUXEN),
+                              pincfg);
+}
+
+static inline void
+ao_enable_output(struct samd21_port *port, uint8_t pin, uint8_t v)
+{
+       ao_enable_port(port);
+       ao_gpio_set(port, pin, v);
+       samd21_port_dir_set(port, pin, SAMD21_PORT_DIR_OUT);
+       samd21_port_pincfg_set(port, pin,
+                              (1 << SAMD21_PORT_PINCFG_DRVSTR) |
+                              (1 << SAMD21_PORT_PINCFG_PULLEN) |
+                              (1 << SAMD21_PORT_PINCFG_INEN),
+                              (0 << SAMD21_PORT_PINCFG_DRVSTR) |
+                              (0 << SAMD21_PORT_PINCFG_PULLEN) |
+                              (0 << SAMD21_PORT_PINCFG_INEN));
+}
+
+static inline void
+ao_enable_input(struct samd21_port *port, uint8_t pin, uint32_t mode)
+{
+       ao_enable_port(port);
+       samd21_port_dir_set(port, pin, SAMD21_PORT_DIR_IN);
+       uint8_t pincfg;
+
+       pincfg = ((0 << SAMD21_PORT_PINCFG_DRVSTR) |
+                 (0 << SAMD21_PORT_PINCFG_PULLEN) |
+                 (1 << SAMD21_PORT_PINCFG_INEN) |
+                 (0 << SAMD21_PORT_PINCFG_PMUXEN));
+
+       if (mode != AO_MODE_PULL_NONE) {
+               pincfg |= (1 << SAMD21_PORT_PINCFG_PULLEN);
+               ao_gpio_set(port, pin, mode == AO_MODE_PULL_UP);
+       }
+
+       samd21_port_pincfg_set(port, pin,
+                              (1 << SAMD21_PORT_PINCFG_DRVSTR) |
+                              (1 << SAMD21_PORT_PINCFG_PULLEN) |
+                              (1 << SAMD21_PORT_PINCFG_INEN) |
+                              (1 << SAMD21_PORT_PINCFG_PMUXEN),
+                              pincfg);
+}
+
+static inline void
+ao_enable_cs(struct samd21_port *port, uint8_t pin)
+{
+       ao_enable_output(port, pin, 1);
+}
+
+/* ao_spi_samd21.c */
+
+#define AO_SPI_INDEX_BIT       0
+#define AO_SPI_INDEX_MASK      0x07
+
+#define AO_SPI_CONFIG_BIT      4
+#define AO_SPI_CONFIG_MASK     (3 << AO_SPI_CONFIG_BIT)
+
+#define AO_SPI_CPOL_BIT                6
+#define AO_SPI_CPHA_BIT                7
+
+#define AO_SPI_DOPO_BIT                8
+#define  AO_SPI_DOPO_MOSI_0_SCLK_1     (0 << AO_SPI_DOPO_BIT)
+#define  AO_SPI_DOPO_MOSI_2_SCLK_3     (1 << AO_SPI_DOPO_BIT)
+#define  AO_SPI_DOPO_MOSI_3_SCLK_1     (2 << AO_SPI_DOPO_BIT)
+#define  AO_SPI_DOPO_MOSI_0_SCLK_3     (3 << AO_SPI_DOPO_BIT)
+#define  AO_SPI_DOPO_MASK              (3 << AO_SPI_DOPO_BIT)
+
+#define AO_SPI_DIPO_BIT                10
+#define  AO_SPI_DIPO_MISO_0            (0 << AO_SPI_DIPO_BIT)
+#define  AO_SPI_DIPO_MISO_1            (1 << AO_SPI_DIPO_BIT)
+#define  AO_SPI_DIPO_MISO_2            (2 << AO_SPI_DIPO_BIT)
+#define  AO_SPI_DIPO_MISO_3            (3 << AO_SPI_DIPO_BIT)
+#define  AO_SPI_DIPO_MASK              (3 << AO_SPI_DIPO_MASK)
+
+#define AO_SPI_CONFIG_0                (0 << AO_SPI_CONFIG_BIT)
+#define AO_SPI_CONFIG_1                (1 << AO_SPI_CONFIG_BIT)
+#define AO_SPI_CONFIG_2                (2 << AO_SPI_CONFIG_BIT)
+#define AO_SPI_CONFIG_3                (3 << AO_SPI_CONFIG_BIT)
+
+#define AO_SPI_INDEX(id)       ((uint8_t) ((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_DOPO(id)                ((uint32_t) (((id) >> AO_SPI_DOPO_BIT) & 3))
+#define AO_SPI_DIPO(id)                ((uint32_t) (((id) >> AO_SPI_DIPO_BIT) & 3))
+
+#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)
+
+#if HAS_SPI_0
+/*
+ * PA08 SERCOM0.0 -> MOSI      (DOPO 0)
+ * PA09 SERCOM0.1 -> SCLK      (DOPO 0)
+ * PA10 SERCOM0.2 -> MISO      (DIPO 2)
+ */
+#define AO_SPI_0_PA08_PA09_PA10        (0 | AO_SPI_CONFIG_0 |          \
+                                AO_SPI_DOPO_MOSI_0_SCLK_1 |    \
+                                AO_SPI_DIPO_MISO_2)
+/*
+ * PA04 SERCOM0.0 -> MOSI      (DOPO 0)
+ * PA05 SERCOM0.1 -> SCLK      (DOPO 0)
+ * PA06 SERCOM0.2 -> MISO      (DIPO 2)
+ */
+#define AO_SPI_0_PA04_PA05_PA06        (0 | AO_SPI_CONFIG_1 |          \
+                                AO_SPI_DOPO_MOSI_0_SCLK_1 |    \
+                                AO_SPI_DIPO_MISO_2)
+#endif /* HAS_SPI_0 */
+
+#if HAS_SPI_3
+/*
+ * PA22 SERCOM3.0 -> MOSI      (DOPO 0)
+ * PA23 SERCOM3.1 -> SCLK      (DOPO 0)
+ * PA20 SERCOM3.2 -> MISO      (DIPO 2)
+ */
+#define AO_SPI_3_PA22_PA23_PA20        (3 | AO_SPI_CONFIG_0 |          \
+                                AO_SPI_DOPO_MOSI_0_SCLK_1 |    \
+                                AO_SPI_DIPO_MISO_2)
+#endif /* HAS_SPI_3 */
+
+#if HAS_SPI_4
+/*
+ * PA04 SERCOM0.0 -> MOSI      (DOPO 0)
+ * PA05 SERCOM0.1 -> SCLK      (DOPO 0)
+ * PA16 SERCOM0.2 -> MISO      (DIPO 2)
+ */
+#define AO_SPI_CONFIG_PA04_PA05_PA06   (0 | AO_SPI_CONFIG_1 |          \
+                                        AO_SPI_DOPO_MOSI_0_SCLK_1 |    \
+                                        AO_SPI_DIPO_MISO_2)
+
+/*
+ * PB10 SERCOM4.2 -> MOSI      (DOPO 1)
+ * PB11 SERCOM4.3 -> SCLK      (DOPO 1)
+ * PA12        SERCOM4.0 -> MISO       (DIPO 0)
+ */
+#define AO_SPI_4_PB10_PB11_PA12        (4 | AO_SPI_CONFIG_0 |          \
+                                AO_SPI_DOPO_MOSI_2_SCLK_3 |    \
+                                AO_SPI_DIPO_MISO_0)
+#endif /* HAS_SPI_4 */
+
+#if HAS_SPI_5
+/*
+ * PB22 SERCOM5.2 -> MOSI      (DOPO 1)
+ * PB23 SERCOM5.3 -> SCLK      (DOPO 1)
+ * PB03 SERCOM5.1 -> MISO      (DIPO 1)
+ */
+#define AO_SPI_5_PB22_PB23_PB03        (5 | AO_SPI_CONFIG_0 |          \
+                                AO_SPI_DOPO_MOSI_2_SCLK_3 |    \
+                                AO_SPI_DIPO_MISO_1)
+#endif /* HAS_SPI_5 */
+
+void
+ao_spi_send(const void *block, uint16_t len, uint16_t spi_index);
+
+void
+ao_spi_send_fixed(uint8_t data, uint16_t len, uint16_t spi_index);
+
+void
+ao_spi_recv(void *block, uint16_t len, uint16_t spi_index);
+
+void
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint16_t spi_index);
+
+void
+ao_spi_get(uint16_t spi_index, uint32_t speed);
+
+void
+ao_spi_put(uint16_t spi_index);
+
+void
+ao_spi_init(void);
+
+#define ao_spi_set_cs(reg,mask) do {           \
+               reg->outclr = mask;             \
+       } while(0)
+
+#define ao_spi_clr_cs(reg,mask) do {           \
+               reg->outset = mask;             \
+       } while(0)
+
+#define ao_spi_get_mask(reg,mask,spi_index, speed) do {                \
+               ao_spi_get(spi_index, speed);                           \
+               ao_spi_set_cs(reg,mask);                        \
+       } while (0)
+
+#define ao_spi_put_mask(reg,mask,spi_index) do {       \
+               ao_spi_clr_cs(reg,mask);        \
+               ao_spi_put(spi_index);          \
+       } while (0)
+
+static inline void
+ao_spi_get_bit(struct samd21_port *port, uint8_t bit, uint16_t spi_index, uint32_t speed)
+{
+       ao_spi_get(spi_index, speed);
+       ao_gpio_set(port, bit, 0);
+}
+
+static inline void
+ao_spi_put_bit(struct samd21_port *port, uint8_t bit, uint16_t spi_index)
+{
+       ao_gpio_set(port, bit, 1);
+       ao_spi_put(spi_index);
+}
+
+static inline uint8_t
+ao_spi_speed(uint32_t hz)
+{
+       int32_t baud = (int32_t) (AO_SYSCLK / (2 * hz)) - 1;
+
+       if (baud < 1)
+               baud = 1;
+       if (baud > 255)
+               baud = 255;
+       return (uint8_t) baud;
+}
+
+#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 ARM_PUSH32(stack, val) (*(--(stack)) = (val))
+
+typedef uint32_t       ao_arch_irq_t;
+
+static inline uint32_t
+ao_arch_irqsave(void) {
+       uint32_t        primask;
+       asm("mrs %0,primask" : "=&r" (primask));
+       ao_arch_block_interrupts();
+       return primask;
+}
+
+static inline void
+ao_arch_irqrestore(uint32_t primask) {
+       asm("msr primask,%0" : : "r" (primask));
+}
+
+static inline void
+ao_arch_memory_barrier(void) {
+       asm volatile("" ::: "memory");
+}
+
+#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-r7 */
+       i = 8;
+       while (i--)
+               ARM_PUSH32(sp, 0);
+
+       /* APSR */
+       ARM_PUSH32(sp, 0);
+
+       /* PRIMASK with interrupts enabled */
+       ARM_PUSH32(sp, 0);
+
+       task->sp32 = sp;
+}
+
+static inline void ao_arch_save_regs(void) {
+       /* Save general registers */
+       asm("push {r0-r7,lr}\n");
+
+       /* Save APSR */
+       asm("mrs r0,apsr");
+       asm("push {r0}");
+
+       /* Save PRIMASK */
+       asm("mrs r0,primask");
+       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);
+       if (sp < &ao_cur_task->stack32[0])
+               ao_panic (AO_PANIC_STACK);
+}
+
+static inline void ao_arch_restore_stack(void) {
+       /* Switch stacks */
+       asm("mov sp, %0" : : "r" (ao_cur_task->sp32) );
+
+       /* Restore PRIMASK */
+       asm("pop {r0}");
+       asm("msr primask,r0");
+
+       /* Restore APSR */
+       asm("pop {r0}");
+       asm("msr apsr_nczvq,r0");
+
+       /* Restore general registers */
+       asm("pop {r0-r7,pc}\n");
+}
+
+#ifndef HAS_SAMPLE_PROFILE
+#define HAS_SAMPLE_PROFILE 0
+#endif
+
+#if !HAS_SAMPLE_PROFILE
+#define HAS_ARCH_START_SCHEDULER       1
+
+static inline void ao_arch_start_scheduler(void) {
+       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");
+}
+#endif
+
+#define ao_arch_isr_stack()
+
+#endif
+
+#define ao_arch_wait_interrupt() do {                          \
+               asm("\twfi\n");                                 \
+               ao_arch_release_interrupts();                   \
+               asm(".global ao_idle_loc\nao_idle_loc:");       \
+               ao_arch_block_interrupts();                     \
+       } while (0)
+
+#define ao_arch_critical(b) do {                       \
+               uint32_t __mask = ao_arch_irqsave();    \
+               do { b } while (0);                     \
+               ao_arch_irqrestore(__mask);             \
+       } while (0)
+
+/* ao_serial_samd21.c */
+
+#if USE_SERIAL_0_FLOW && USE_SERIAL_0_SW_FLOW || USE_SERIAL_1_FLOW && USE_SERIAL_1_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
+#define USE_SERIAL_1_HW_FLOW   1
+#endif
+
+#if USE_SERIAL_0_FLOW && !USE_SERIAL_0_SW_FLOW
+#define USE_SERIAL_0_HW_FLOW   1
+#endif
+
+#if USE_SERIAL_0_HW_FLOW || USE_SERIAL_1_HW_FLOW
+#define HAS_SERIAL_HW_FLOW     1
+#else
+#define HAS_SERIAL_HW_FLOW     0
+#endif
+
+struct ao_samd21_usart {
+       struct ao_fifo          rx_fifo;
+       struct ao_fifo          tx_fifo;
+       struct samd21_sercom    *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 samd21_port      *gpio_rts;
+       struct samd21_port      *gpio_cts;
+       uint8_t                 pin_rts;
+       uint8_t                 pin_cts;
+       uint8_t                 rts;
+#endif
+};
+
+#if HAS_USART_0
+extern struct ao_samd21_usart  ao_samd21_usart0;
+#endif
+
+void
+ao_serial_init(void);
+
+/* ao_usb_samd21.c */
+
+#if AO_USB_OUT_HOOK
+void
+ao_usb_out_hook(uint8_t *buffer, uint16_t count);
+#endif
+
+void start(void);
+
+#endif /* _AO_ARCH_FUNCS_H_ */
diff --git a/src/samd21/ao_beep_samd21.c b/src/samd21/ao_beep_samd21.c
new file mode 100644 (file)
index 0000000..7fbbcba
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_beep.h"
+
+#define BEEP_SCALE     (AO_SYSCLK / 750000)
+
+void
+ao_beep(uint8_t beep)
+{
+       struct samd21_tcc *tcc = AO_BEEP_TCC;
+       if (beep) {
+               tcc->per = (beep * BEEP_SCALE);
+               tcc->ctrla = (1 << SAMD21_TCC_CTRLA_ENABLE);
+       } else {
+               tcc->ctrla = (0 << SAMD21_TCC_CTRLA_ENABLE);
+       }
+}
+
+void
+ao_beep_for(uint8_t beep, AO_TICK_TYPE ticks)
+{
+       ao_beep(beep);
+       ao_delay(ticks);
+       ao_beep(0);
+}
+
+static void
+ao_tcc_init(struct samd21_tcc *tcc, uint32_t apbcmask)
+{
+       samd21_pm.apbcmask |= apbcmask;
+
+       /* Reset the device */
+       tcc->ctrla = (1 << SAMD21_TCC_CTRLA_SWRST);
+
+       while ((tcc->ctrla & (1 << SAMD21_TCC_CTRLA_SWRST)) != 0 ||
+              (tcc->syncbusy & (1 << SAMD21_TCC_SYNCBUSY_SWRST)) != 0)
+               ;
+
+       tcc->per = 94 * BEEP_SCALE;
+
+       tcc->wave = ((SAMD21_TCC_WAVE_WAVEGEN_NFRQ << SAMD21_TCC_WAVE_WAVEGEN) |
+                    (0 << SAMD21_TCC_WAVE_RAMP) |
+                    (0 << SAMD21_TCC_WAVE_CIPEREN) |
+                    (0 << SAMD21_TCC_WAVE_CCCEN(0)) |
+                    (0 << SAMD21_TCC_WAVE_CCCEN(1)) |
+                    (0 << SAMD21_TCC_WAVE_CCCEN(2)) |
+                    (0 << SAMD21_TCC_WAVE_CCCEN(3)) |
+                    (0 << SAMD21_TCC_WAVE_POL(0)) |
+                    (0 << SAMD21_TCC_WAVE_POL(1)) |
+                    (0 << SAMD21_TCC_WAVE_POL(1)) |
+                    (0 << SAMD21_TCC_WAVE_POL(3)) |
+                    (0 << SAMD21_TCC_WAVE_SWAP(0)) |
+                    (0 << SAMD21_TCC_WAVE_SWAP(1)) |
+                    (0 << SAMD21_TCC_WAVE_SWAP(1)) |
+                    (0 << SAMD21_TCC_WAVE_SWAP(3)));
+
+       tcc->dbgctrl = (1 << SAMD21_TCC_DBGCTRL_DBGRUN);
+}
+
+void
+ao_beep_init(void)
+{
+       struct samd21_port      *port = AO_BEEP_PORT;
+       uint8_t                 pin = AO_BEEP_PIN;
+       struct samd21_tcc       *tcc = AO_BEEP_TCC;
+       uint32_t                apbc_mask = 1UL << AO_BEEP_TCC_APBC_MASK;
+
+       if (tcc == &samd21_tcc0 || tcc == &samd21_tcc1) {
+               samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC0_TCC1);
+       } else {
+               samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC2_TC3);
+       }
+
+       ao_tcc_init(tcc, apbc_mask);
+
+       ao_enable_output(port, pin, 0);
+
+       samd21_port_pmux_set(port, pin, AO_BEEP_FUNC);
+}
diff --git a/src/samd21/ao_boot_chain.c b/src/samd21/ao_boot_chain.c
new file mode 100644 (file)
index 0000000..551af78
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_boot.h>
+
+void
+ao_boot_chain(uint32_t *base)
+{
+       uint32_t        sp;
+       uint32_t        pc;
+
+       sp = base[0];
+       pc = base[1];
+       if (0x00000100 <= pc && pc <= 0x00200000 && (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();
+}
diff --git a/src/samd21/ao_boot_pin.c b/src/samd21/ao_boot_pin.c
new file mode 100644 (file)
index 0000000..baabe81
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_boot.h>
+#include <ao_exti.h>
+
+int
+ao_boot_check_pin(void)
+{
+       uint16_t v;
+
+       /* 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);
+       return v == AO_BOOT_APPLICATION_VALUE;
+}
diff --git a/src/samd21/ao_dac_samd21.c b/src/samd21/ao_dac_samd21.c
new file mode 100644 (file)
index 0000000..7dc00c1
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <snek.h>
+#include <ao.h>
+#include <ao_dac-samd21.h>
+#include <ao_tcc-samd21.h>
+
+/* Max DAC output value. We're using left-adjusted values */
+#define SNEK_DAC_MAX           65535
+
+#ifdef SNEK_SAMD21_DAC_TIMER
+/*
+ * If there's a timer available, we can use that
+ * to implement the 'tone' function
+ */
+
+#include "sine.h"
+
+#define NSINE  (sizeof(sine) / sizeof(sine[0]))
+
+static uint16_t current_power;
+static uint16_t power;
+static uint32_t        phase;
+static uint32_t phase_step;
+static volatile bool dac_running;
+
+#define _paste2(x,y)   x ## y
+#define _paste3(x,y,z) x ## y ## z
+#define paste2(x,y)    _paste2(x,y)
+#define paste3(x,y,z)  _paste3(x,y,z)
+#define SAMD21_TCC     paste2(samd21_tcc, SNEK_SAMD21_DAC_TIMER)
+#define SAMD21_TCC_ISR paste3(samd21_tcc, SNEK_SAMD21_DAC_TIMER, _isr)
+
+#define AO_DAC_RATE    24000
+
+#define UINT_TO_FIXED(u)       ((uint32_t) (u) << 16)
+#define FIXED_TO_UINT(u)       ((u) >> 16)
+
+void
+SAMD21_TCC_ISR(void)
+{
+       uint32_t intflag = SAMD21_TCC.intflag;
+       SAMD21_TCC.intflag = intflag;
+       if (intflag & (1 << SAMD21_TCC_INTFLAG_OVF)) {
+               if (phase_step) {
+                       samd21_dac.data = ((uint32_t) sine[FIXED_TO_UINT(phase)] * current_power) >> 16;
+                       if ((phase += phase_step) >= UINT_TO_FIXED(NSINE)) {
+                               phase -= UINT_TO_FIXED(NSINE);
+
+                               current_power = power;
+
+                               /* Stop output at zero crossing when no longer outputing tone */
+                               if (!dac_running) {
+                                       phase_step = 0;
+                                       phase = 0;
+                                       SAMD21_TCC.intenclr = (1 << SAMD21_TCC_INTFLAG_OVF);
+                               }
+                       }
+               }
+       }
+}
+
+void
+ao_dac_set_hz(float hz)
+{
+       /* samples/second = AC_DAC_RATE
+        *
+        * cycles/second = hz
+        *
+        * samples/cycle = AC_DAC_RATE / hz
+        *
+        * step/cycle = 256
+        *
+        * step/sample = step/cycle * cycle/samples
+        *             = TWO_PI * hz / AC_DAC_RATE;
+        */
+       uint32_t new_phase_step = (float) UINT_TO_FIXED(NSINE) * hz / (float) AO_DAC_RATE;
+       ao_arch_critical(
+               if (new_phase_step) {
+                       dac_running = true;
+                       phase_step = new_phase_step;
+                       SAMD21_TCC.intenset = (1 << SAMD21_TCC_INTFLAG_OVF);
+               } else {
+                       dac_running = false;
+               });
+}
+
+static void
+ao_dac_timer_init(void)
+{
+       /* Adjust timer to interrupt once per sample period */
+       SAMD21_TCC.per = AO_HCLK / AO_DAC_RATE;
+
+       /* Enable timer interrupts */
+       samd21_nvic_set_enable(paste3(SAMD21_NVIC_ISR_TCC, SNEK_SAMD21_DAC_TIMER, _POS));
+       samd21_nvic_set_priority(paste3(SAMD21_NVIC_ISR_TCC, SNEK_SAMD21_DAC_TIMER, _POS), 3);
+}
+#else
+#define ao_dac_timer_init()
+#endif
+
+static void
+ao_dac_sync(void)
+{
+       while (samd21_dac.status & (1 << SAMD21_DAC_STATUS_SYNCBUSY))
+               ;
+}
+
+void
+ao_dac_set(uint16_t new_power)
+{
+#if SNEK_DAC_MAX != SNEK_PWM_MAX
+       new_power = (uint16_t) ((uint32_t) new_power * SNEK_DAC_MAX) / SNEK_PWM_MAX;
+#endif
+
+       ao_arch_critical(
+#ifdef SNEK_SAMD21_DAC_TIMER
+               power = new_power;
+               /*
+                * When not generating a tone, just set the DAC
+                * output to the requested level
+                */
+               if (!phase_step) {
+                       current_power = new_power;
+                       samd21_dac.data = new_power;
+               }
+#else
+               samd21_dac.data = new_power;
+#endif
+               );
+}
+
+void
+ao_dac_init(void)
+{
+       /* supply a clock */
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_DAC);
+
+       /* enable the device */
+       samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_DAC);
+
+       /* reset */
+       samd21_dac.ctrla = (1 << SAMD21_DAC_CTRLA_SWRST);
+
+       while ((samd21_dac.ctrla & (1 << SAMD21_DAC_CTRLA_SWRST)) != 0 ||
+              (samd21_dac.status & (1 << SAMD21_DAC_STATUS_SYNCBUSY)) != 0)
+               ao_arch_nop();
+
+       /* Configure using VDD as reference */
+       samd21_dac.ctrlb = ((1 << SAMD21_DAC_CTRLB_EOEN) |
+                           (0 << SAMD21_DAC_CTRLB_IOEN) |
+                           (1 << SAMD21_DAC_CTRLB_LEFTADJ) |
+                           (0 << SAMD21_DAC_CTRLB_VPD) |
+                           (1 << SAMD21_DAC_CTRLB_BDWP) |
+                           (SAMD21_DAC_CTRLB_REFSEL_VDDANA << SAMD21_DAC_CTRLB_REFSEL));
+
+       ao_dac_sync();
+
+       samd21_dac.ctrla = (1 << SAMD21_DAC_CTRLA_ENABLE);
+
+       ao_dac_sync();
+
+       ao_dac_timer_init();
+}
diff --git a/src/samd21/ao_dac_samd21.h b/src/samd21/ao_dac_samd21.h
new file mode 100644 (file)
index 0000000..7939f4b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _AO_DAC_SAMD21_H_
+#define _AO_DAC_SAMD21_H_
+
+void
+ao_dac_set(uint16_t value);
+
+#ifdef SNEK_SAMD21_DAC_TIMER
+void
+ao_dac_set_hz(float hz);
+#endif
+
+void
+ao_dac_init(void);
+
+#endif /* _AO_DAC_SAMD21_H_ */
diff --git a/src/samd21/ao_dma_samd21.c b/src/samd21/ao_dma_samd21.c
new file mode 100644 (file)
index 0000000..5bf9470
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright © 2019 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <ao.h>
+#include <ao_dma_samd21.h>
+
+static struct samd21_dmac_desc samd21_dmac_desc[SAMD21_DMAC_NCHAN]  __attribute__((aligned(16)));
+static struct samd21_dmac_desc samd21_dmac_wrb[SAMD21_DMAC_NCHAN]  __attribute__((aligned(16)));
+
+static struct {
+       void (*callback)(uint8_t id, void *closure);
+       void *closure;
+} dmac_callback[SAMD21_DMAC_NCHAN];
+
+void
+samd21_dmac_isr(void)
+{
+       uint16_t        intpend = samd21_dmac.intpend;
+
+       if (intpend & 0x0700) {
+               uint8_t id = (intpend >> SAMD21_DMAC_INTPEND_ID) & SAMD21_DMAC_INTPEND_ID_MASK;
+               samd21_dmac.intpend = intpend;
+               if (intpend & (1 << SAMD21_DMAC_INTPEND_TCMPL)) {
+                       if (dmac_callback[id].callback)
+                               (*dmac_callback[id].callback)(id, dmac_callback[id].closure);
+               }
+       }
+}
+
+void
+ao_dma_dump(char *where)
+{
+       printf("DMA %s ctrl %04x intpend %04x intstatus %04lx\n",
+              where,
+              samd21_dmac.ctrl,
+              samd21_dmac.intpend,
+              samd21_dmac.intstatus);
+       fflush(stdout);
+       printf(" busych %04lx pendch %04lx active %08lx chctrla %02x\n",
+              samd21_dmac.busych,
+              samd21_dmac.pendch,
+              samd21_dmac.active,
+              samd21_dmac.chctrla);
+       fflush(stdout);
+       printf(" chctrlb %08lx chintflag %02x chstatus %02x\n",
+              samd21_dmac.chctrlb,
+              samd21_dmac.chintflag,
+              samd21_dmac.chstatus);
+       fflush(stdout);
+       printf(" btctrl %04x btcnt %04x srcaddr %08lx dstaddr %08lx descaddr %08lx\n",
+              samd21_dmac_desc[0].btctrl,
+              samd21_dmac_desc[0].btcnt,
+              samd21_dmac_desc[0].srcaddr,
+              samd21_dmac_desc[0].dstaddr,
+              samd21_dmac_desc[0].descaddr);
+       fflush(stdout);
+}
+
+void
+_ao_dma_start_transfer(uint8_t         id,
+                      const void       *src,
+                      void             *dst,
+                      uint16_t         count,
+                      uint32_t         chctrlb,
+                      uint16_t         btctrl,
+                      void             (*callback)(uint8_t id, void *closure),
+                      void             *closure)
+{
+       /* Set up the callback */
+       dmac_callback[id].closure = closure;
+       dmac_callback[id].callback = callback;
+
+       /* Set up the descriptor */
+       samd21_dmac_desc[id].btctrl = btctrl;
+       samd21_dmac_desc[id].btcnt = count;
+       samd21_dmac_desc[id].srcaddr = (uint32_t) src;
+       samd21_dmac_desc[id].dstaddr = (uint32_t) dst;
+       samd21_dmac_desc[id].descaddr = 0;
+
+       /* Configure the channel and enable it */
+       samd21_dmac.chid = id;
+       samd21_dmac.chctrlb = chctrlb;
+       samd21_dmac.chctrla = (1 << SAMD21_DMAC_CHCTRLA_ENABLE);
+}
+
+void
+_ao_dma_done_transfer(uint8_t id)
+{
+       /* Disable channel */
+       samd21_dmac.chid = id;
+       samd21_dmac.chctrla = 0;
+}
+
+void
+ao_dma_init(void)
+{
+       uint8_t ch;
+
+       /* Enable DMAC clocks */
+       samd21_pm.ahbmask |= (1 << SAMD21_PM_AHBMASK_DMAC);
+       samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_DMAC);
+
+#if 1
+       /* Enable HPB clocks so we can talk to peripherals */
+       samd21_pm.ahbmask |= ((1 << SAMD21_PM_AHBMASK_HPB0) |
+                             (1 << SAMD21_PM_AHBMASK_HPB1) |
+                             (1 << SAMD21_PM_AHBMASK_HPB2));
+#endif
+
+       samd21_pm.apbamask |= (1 << SAMD21_PM_APBAMASK_PAC0);
+       samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_PAC1);
+       samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_PAC2);
+
+       /* Reset DMAC device */
+       samd21_dmac.ctrl = 0;
+       samd21_dmac.ctrl = (1 << SAMD21_DMAC_CTRL_SWRST);
+       while (samd21_dmac.ctrl & (1 << SAMD21_DMAC_CTRL_SWRST))
+               ;
+
+       samd21_dmac.baseaddr = (uint32_t) &samd21_dmac_desc[0];
+       samd21_dmac.wrbaddr = (uint32_t) &samd21_dmac_wrb[0];
+
+       samd21_dmac.swtrigctrl = 0;
+
+       /* Set QoS to highest value */
+       samd21_dmac.qosctrl = ((SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_DQOS) |
+                              (SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_FQOS) |
+                              (SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_WRBQOS));
+
+       /* Enable DMAC controller with all priority levels */
+       samd21_dmac.ctrl = ((1 << SAMD21_DMAC_CTRL_DMAENABLE) |
+                           (1 << SAMD21_DMAC_CTRL_LVLEN(0)) |
+                           (1 << SAMD21_DMAC_CTRL_LVLEN(1)) |
+                           (1 << SAMD21_DMAC_CTRL_LVLEN(2)) |
+                           (1 << SAMD21_DMAC_CTRL_LVLEN(3)));
+
+       /* Reset all DMAC channels */
+       for (ch = 0; ch < SAMD21_DMAC_NCHAN; ch++) {
+               samd21_dmac.chid = ch;
+               samd21_dmac.chctrla = (1 << SAMD21_DMAC_CHCTRLA_SWRST);
+               while (samd21_dmac.chctrla & (1 << SAMD21_DMAC_CHCTRLA_SWRST))
+                       ;
+               samd21_dmac.chintenset = (1 << SAMD21_DMAC_CHINTFLAG_TCMPL);
+       }
+
+       /* configure interrupts */
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_DMAC_POS);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_DMAC_POS, 3);
+}
diff --git a/src/samd21/ao_dma_samd21.h b/src/samd21/ao_dma_samd21.h
new file mode 100644 (file)
index 0000000..57f5476
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2019 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _AO_DMA_SAM21_H_
+#define _AO_DMA_SAM21_H_
+
+void
+ao_dma_init(void);
+
+void
+_ao_dma_start_transfer(uint8_t         id,
+                      const void       *src,
+                      void             *dst,
+                      uint16_t         count,
+                      uint32_t         chctrlb,
+                      uint16_t         btctrl,
+                      void             (*callback)(uint8_t id, void *closure),
+                      void             *closure);
+
+void
+_ao_dma_done_transfer(uint8_t id);
+
+void
+ao_dma_dump(char *where);
+
+/*
+ * DMA is only used for SERCOM
+ */
+
+#define AO_SERCOM_DMA_BASE                     0U
+#define AO_SERCOM_INPUT_DMA_ID(id)             ((uint8_t) ((id) * 2U + 0U + AO_SERCOM_DMA_BASE))
+#define AO_SERCOM_OUTPUT_DMA_ID(id)            ((uint8_t) ((id) * 2U + 1U + AO_SERCOM_DMA_BASE))
+
+#endif /* _AO_DMA_SAM21_H_ */
diff --git a/src/samd21/ao_exti.h b/src/samd21/ao_exti.h
new file mode 100644 (file)
index 0000000..1bd14a0
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_EXTI_H_
+#define _AO_EXTI_H_
+
+#define AO_EXTI_MODE_RISING    SAMD21_EIC_CONFIG_SENSE_RISE
+#define AO_EXTI_MODE_FALLING   SAMD21_EIC_CONFIG_SENSE_FALL
+#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
+
+void
+ao_exti_setup(struct samd21_port *gpio, uint8_t pin, uint8_t mode, void (*callback)(void));
+
+void
+ao_exti_set_mode(struct samd21_port *gpio, uint8_t pin, uint8_t mode);
+
+void
+ao_exti_set_callback(struct samd21_port *gpio, uint8_t pin, void (*callback)(void));
+
+void
+ao_exti_enable(struct samd21_port *gpio, uint8_t pin);
+
+void
+ao_exti_disable(struct samd21_port *gpio, uint8_t pin);
+
+void
+ao_exti_init(void);
+
+#endif /* _AO_EXTI_H_ */
diff --git a/src/samd21/ao_exti_samd21.c b/src/samd21/ao_exti_samd21.c
new file mode 100644 (file)
index 0000000..688f643
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+
+struct ao_samd21_exti {
+       void                    (*callback)(void);
+       uint8_t                 pmux;
+       uint8_t                 pincfg;
+};
+
+static struct ao_samd21_exti ao_samd21_exti[SAMD21_NUM_EIC];
+
+static uint8_t
+pin_id(struct samd21_port *port, uint8_t pin)
+{
+       /* Why, atmel, why? */
+       if (port == &samd21_port_a) {
+               switch (pin) {
+               case 24:
+               case 25:
+               case 27:
+                       return pin - 12;
+               case 28:
+               case 30:
+               case 31:
+                       return pin - 20;
+               default:
+                       break;
+               }
+       }
+
+       /* most pins use exti mapped to their pin number directly (mod 16) */
+       return pin & 0xf;
+}
+
+
+void
+samd21_eic_isr(void)
+{
+       uint32_t        intflag = samd21_eic.intflag;
+       uint8_t         id;
+
+       for (id = 0; id < SAMD21_NUM_EIC; id++) {
+               uint32_t mask = (1 << id);
+
+               if (intflag & mask) {
+                       samd21_eic.intflag = mask;
+                       if (ao_samd21_exti[id].callback)
+                               (*ao_samd21_exti[id].callback)();
+               }
+       }
+}
+
+static void
+_ao_exti_set_sense(uint8_t id, uint8_t mode)
+{
+       uint8_t         sense = mode & (SAMD21_EIC_CONFIG_SENSE_RISE | SAMD21_EIC_CONFIG_SENSE_FALL);
+       uint8_t         n = SAMD21_EIC_CONFIG_N(id);
+       uint32_t        config;
+
+       config = samd21_eic.config[n];
+       config &= ~(SAMD21_EIC_CONFIG_SENSE_MASK << SAMD21_EIC_CONFIG_SENSE(id));
+       config |= (sense << SAMD21_EIC_CONFIG_SENSE(id));
+       samd21_eic.config[n] = config;
+}
+
+void
+ao_exti_setup (struct samd21_port *port, uint8_t pin, uint8_t mode, void (*callback)(void))
+{
+       uint8_t                 id = pin_id(port,pin);
+       struct ao_samd21_exti   *exti = &ao_samd21_exti[id];
+
+       if (exti->callback)
+               ao_panic(AO_PANIC_EXTI);
+
+       if (mode & AO_EXTI_PIN_NOCONFIGURE) {
+               ao_enable_port(port);
+               samd21_port_dir_set(port, pin, SAMD21_PORT_DIR_IN);
+               samd21_port_pincfg_set(port, pin,
+                                      (1 << SAMD21_PORT_PINCFG_INEN),
+                                      (1 << SAMD21_PORT_PINCFG_INEN));
+       } else {
+               ao_enable_input(port, pin, mode);
+       }
+
+       ao_arch_block_interrupts();
+
+       exti->callback = callback;
+
+       /* Set edge triggered */
+       _ao_exti_set_sense(id, mode);
+
+       ao_arch_release_interrupts();
+}
+
+void
+ao_exti_set_mode(struct samd21_port *port, uint8_t pin, uint8_t mode)
+{
+       uint8_t                 id = pin_id(port,pin);
+
+       ao_arch_block_interrupts();
+       _ao_exti_set_sense(id, mode);
+       ao_arch_release_interrupts();
+}
+
+void
+ao_exti_set_callback(struct samd21_port *port, uint8_t pin, void (*callback)(void))
+{
+       uint8_t         id = pin_id(port,pin);
+
+       ao_arch_block_interrupts();
+       ao_samd21_exti[id].callback = callback;
+       ao_arch_release_interrupts();
+}
+
+void
+ao_exti_enable(struct samd21_port *port, uint8_t pin)
+{
+       uint8_t         id = pin_id(port,pin);
+
+       ao_arch_block_interrupts();
+       /* configure gpio to interrupt routing */
+       if ((samd21_eic.intenset & (1 << id)) == 0) {
+               ao_samd21_exti[id].pmux = samd21_port_pmux_get(port, pin);
+               ao_samd21_exti[id].pincfg = samd21_port_pincfg_get(port, pin);
+       }
+       samd21_port_pmux_set(port, pin, SAMD21_PORT_PMUX_FUNC_A);
+       samd21_eic.intenset = 1 << id;
+       ao_arch_release_interrupts();
+}
+
+void
+ao_exti_disable(struct samd21_port *port, uint8_t pin)
+{
+       uint8_t         id = pin_id(port,pin);
+
+       ao_arch_block_interrupts();
+       samd21_eic.intenclr = 1 << id;
+       /* restore gpio config */
+       if (ao_samd21_exti[id].pincfg & (1 << SAMD21_PORT_PINCFG_PMUXEN))
+               samd21_port_pmux_set(port, pin, ao_samd21_exti[id].pmux);
+       else
+               samd21_port_pmux_clr(port, pin);
+       ao_arch_release_interrupts();
+}
+
+void
+ao_exti_init(void)
+{
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_EIC);
+
+       /* Reset */
+       samd21_eic.ctrl = (1 << SAMD21_EIC_CTRL_SWRST);
+
+       while (samd21_eic.status & (1 << SAMD21_EIC_STATUS_SYNCBUSY))
+               ;
+
+       /* Wire up interrupts */
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_EIC_POS);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_EIC_POS, 3);
+
+       /* Enable */
+       samd21_eic.ctrl = (1 << SAMD21_EIC_CTRL_ENABLE);
+}
diff --git a/src/samd21/ao_flash.h b/src/samd21/ao_flash.h
new file mode 100644 (file)
index 0000000..5922625
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright © 2019 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_H_
+#define _AO_FLASH_H_
+
+void
+ao_flash_erase_page(uint32_t *page);
+
+void
+ao_flash_page(uint32_t *page, uint32_t *src);
+
+#endif /* _AO_FLASH_H_ */
diff --git a/src/samd21/ao_flash_loader_samd21.c b/src/samd21/ao_flash_loader_samd21.c
new file mode 100644 (file)
index 0000000..8b08ab2
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_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;
+}
diff --git a/src/samd21/ao_flash_samd21.c b/src/samd21/ao_flash_samd21.c
new file mode 100644 (file)
index 0000000..502ff35
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright © 2019 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>
+#include <stdio.h>
+
+/* Erase rows are four pages */
+static uint32_t
+samd21_nvmctrl_row_size(void)
+{
+       return samd21_nvmctrl_page_size() * 4;
+}
+
+/* size of a lock region. That's just total flash size / 16 */
+static uint32_t
+samd21_nvmctrl_lock_region(void)
+{
+       return samd21_flash_size() >> 4;
+}
+
+/* Find the bit index of an address within the lock word */
+static uint8_t
+ao_flash_lock_region_bit(void *addr)
+{
+       uint32_t lock_region = samd21_nvmctrl_lock_region();
+       uintptr_t a = (uintptr_t) addr;
+
+       while (lock_region) {
+               a >>= 1;
+               lock_region >>= 1;
+       }
+
+       return (uint8_t) a;
+}
+
+static uint8_t
+ao_flash_is_locked(void *addr)
+{
+       return (samd21_nvmctrl.lock >> ao_flash_lock_region_bit(addr)) & 1;
+}
+
+/* Execute a single flash operation, waiting for it to complete. This
+ * bit of code must be in ram
+ */
+static void __attribute__ ((section(".sdata2.flash"), noinline))
+_ao_flash_execute(uint16_t cmd)
+{
+       while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0)
+               ;
+       samd21_nvmctrl.ctrla = ((cmd << SAMD21_NVMCTRL_CTRLA_CMD) |
+                               (SAMD21_NVMCTRL_CTRLA_CMDEX_KEY << SAMD21_NVMCTRL_CTRLA_CMDEX));
+       while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0)
+               ;
+       samd21_nvmctrl.intflag = ((1 << SAMD21_NVMCTRL_INTFLAG_READY) |
+                                 (1 << SAMD21_NVMCTRL_INTFLAG_ERROR));
+}
+
+/* Set the address of the next flash operation */
+static void
+_ao_flash_set_addr(void *addr)
+{
+       while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0)
+               ;
+       samd21_nvmctrl.addr = ((uint32_t) addr) >> 1;
+       while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0)
+               ;
+}
+
+/* Unlock a region of flash */
+static void
+_ao_flash_unlock(void *addr)
+{
+       if (!ao_flash_is_locked(addr))
+               return;
+
+       _ao_flash_set_addr(addr);
+       _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_UR);
+}
+
+/* Erase a row of flash */
+static void
+_ao_flash_erase_row(void *row)
+{
+       _ao_flash_unlock(row);
+       _ao_flash_set_addr(row);
+       _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_ER);
+}
+
+void
+ao_flash_erase_page(uint32_t *page)
+{
+       uint8_t *row = (uint8_t *) page;
+       uint32_t row_size = samd21_nvmctrl_row_size();
+       uint32_t rows = (row_size + 255) / 256;
+
+       if ((uintptr_t) page & (row_size - 1))
+               return;
+
+       ao_arch_block_interrupts();
+
+       if (((uintptr_t) row & (row_size - 1)) == 0) {
+               while (rows--) {
+                       _ao_flash_erase_row(row);
+                       row += row_size;
+               }
+       }
+
+       ao_arch_release_interrupts();
+}
+
+void
+ao_flash_page(uint32_t *page, uint32_t *src)
+{
+       uint32_t        page_shift = samd21_nvmctrl_page_shift();
+       uint32_t        pages = 256 >> page_shift;
+       uint32_t        i;
+       uint32_t        per_page = 1 << (page_shift - 2);
+
+       ao_flash_erase_page(page);
+
+       ao_arch_block_interrupts();
+
+       while(pages--) {
+               /* Clear write buffer */
+               _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_PBC);
+               _ao_flash_set_addr(page);
+               for (i = 0; i < per_page; i++)
+                       *page++ = *src++;
+               _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_WP);
+       }
+
+       ao_arch_release_interrupts();
+}
+
diff --git a/src/samd21/ao_flash_samd21_pins.h b/src/samd21/ao_flash_samd21_pins.h
new file mode 100644 (file)
index 0000000..2293968
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_FLASH_SAMD21_PINS_H_
+#define _AO_FLASH_SAMD21_PINS_H_
+
+#include <ao_flash_pins.h>
+
+#define AO_AHB_PRESCALER       1
+#define AO_APBA_PRESCALER      1
+
+#define HAS_USB                1
+
+#endif /* _AO_FLASH_SAMD21_PINS_H_ */
diff --git a/src/samd21/ao_interrupt.c b/src/samd21/ao_interrupt.c
new file mode 100644 (file)
index 0000000..e425520
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright © 2019 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.
+ */
+
+#include <ao.h>
+#include <ao_boot.h>
+
+/* Interrupt functions */
+
+void samd21_halt_isr(void)
+{
+       ao_panic(AO_PANIC_CRASH);
+}
+
+void samd21_ignore_isr(void)
+{
+}
+
+uint32_t
+samd21_flash_size(void)
+{
+       uint32_t        nvmp = (samd21_nvmctrl.param >> SAMD21_NVMCTRL_PARAM_NVMP) & SAMD21_NVMCTRL_PARAM_NVMP_MASK;
+       uint32_t        psz = (samd21_nvmctrl.param >> SAMD21_NVMCTRL_PARAM_PSZ) & SAMD21_NVMCTRL_PARAM_PSZ_MASK;
+
+       /* page size is 2**(3 + psz) */
+       return nvmp << (3 + psz);
+}
+
+#define STRINGIFY(x) #x
+
+#define isr(name) \
+       void __attribute__ ((weak)) samd21_ ## name ## _isr(void); \
+       _Pragma(STRINGIFY(weak samd21_ ## name ## _isr = samd21_ignore_isr))
+
+#define isr_halt(name) \
+       void __attribute__ ((weak)) samd21_ ## name ## _isr(void); \
+       _Pragma(STRINGIFY(weak samd21_ ## name ## _isr = samd21_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(pm);               /* IRQ0 */
+isr(sysctrl);
+isr(wdt);
+isr(rtc);
+isr(eic);
+isr(nvmctrl);
+isr(dmac);
+isr(usb);
+isr(evsys);
+isr(sercom0);
+isr(sercom1);
+isr(sercom2);
+isr(sercom3);
+isr(sercom4);
+isr(sercom5);
+isr(tcc0);
+isr(tcc1);
+isr(tcc2);
+isr(tc3);
+isr(tc4);
+isr(tc5);
+isr(tc6);
+isr(tc7);
+isr(adc);
+isr(ac);
+isr(dac);
+isr(ptc);
+isr(i2s);
+isr(ac1);
+isr(tcc3);
+
+#undef isr
+#undef isr_halt
+
+#define i(addr,name)   [(addr)/4] = samd21_ ## name ## _isr
+
+extern char __stack[];
+void _start(void) __attribute__((__noreturn__));
+void main(void) __attribute__((__noreturn__));
+
+__attribute__ ((section(".init")))
+void (*const __interrupt_vector[])(void) __attribute((aligned(128))) = {
+       [0] = (void *) __stack,
+       [1] = _start,
+       i(0x08, nmi),
+       i(0x0c, hardfault),
+       i(0x2c, svc),
+       i(0x30, debugmon),
+       i(0x38, pendsv),
+       i(0x3c, systick),
+
+       i(0x40, pm),            /* IRQ0 */
+       i(0x44, sysctrl),
+       i(0x48, wdt),
+       i(0x4c, rtc),
+       i(0x50, eic),
+       i(0x54, nvmctrl),
+       i(0x58, dmac),
+       i(0x5c, usb),
+       i(0x60, evsys),
+       i(0x64, sercom0),
+       i(0x68, sercom1),
+       i(0x6c, sercom2),
+       i(0x70, sercom3),
+       i(0x74, sercom4),
+       i(0x78, sercom5),
+       i(0x7c, tcc0),
+       i(0x80, tcc1),
+       i(0x84, tcc2),
+       i(0x88, tc3),
+       i(0x8c, tc4),
+       i(0x90, tc5),
+       i(0x94, tc6),
+       i(0x98, tc7),
+       i(0x9c, adc),
+       i(0xa0, ac),
+       i(0xa4, dac),
+       i(0xa8, ptc),
+       i(0xac, i2s),
+       i(0xb0, ac1),
+       i(0xb4, tcc3),
+};
+
+extern char __data_source[];
+extern char __data_start[];
+extern char __data_size[];
+extern char __bss_start[];
+extern char __bss_size[];
+
+void _start(void)
+{
+       memcpy(__data_start, __data_source, (uintptr_t) __data_size);
+       memset(__bss_start, '\0', (uintptr_t) __bss_size);
+
+#if AO_BOOT_CHAIN
+       if (ao_boot_check_chain()) {
+#if AO_BOOT_PIN
+               if (ao_boot_check_pin())
+#endif
+               {
+                       ao_boot_chain(AO_BOOT_APPLICATION_BASE);
+               }
+       }
+#endif
+
+       /* Turn on sysctrl */
+       samd21_pm.apbamask |= (1 << SAMD21_PM_APBAMASK_SYSCTRL);
+       /* Set interrupt vector */
+       samd21_scb.vtor = (uint32_t) &__interrupt_vector;
+
+       main();
+}
diff --git a/src/samd21/ao_neopixel.c b/src/samd21/ao_neopixel.c
new file mode 100644 (file)
index 0000000..818910c
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2019 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <ao.h>
+#include <ao_snek.h>
+
+void
+ao_snek_neopixel_write(void *gpio, uint8_t pin, int npixel, const struct snek_neopixel *pixels)
+{
+       volatile uint32_t *outtgl = &(((struct samd21_port *) gpio)->outtgl);
+       uint32_t value = ((uint32_t) 1 << pin);
+
+       while (npixel--) {
+               int32_t p = pixels->p;
+               uint8_t bit;
+               pixels++;
+
+               ao_arch_block_interrupts();
+               for (bit = 0; bit < 24; bit++) {
+                       *outtgl = value;
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       if (p < 0) {
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                               *outtgl = value;
+                       } else {
+                               *outtgl = value;
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                       }
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+
+                       ao_arch_nop();
+
+                       p <<= 1;
+               }
+               ao_arch_release_interrupts();
+       }
+}
diff --git a/src/samd21/ao_serial.h b/src/samd21/ao_serial.h
new file mode 100644 (file)
index 0000000..1120f65
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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_SERIAL_H_
+#define _AO_SERIAL_H_
+
+#define AO_SERIAL_SPEED_4800   0
+#define AO_SERIAL_SPEED_9600   1
+#define AO_SERIAL_SPEED_19200  2
+#define AO_SERIAL_SPEED_57600  3
+#define AO_SERIAL_SPEED_115200 4
+
+#if HAS_SERIAL_0
+extern struct ao_samd21_usart  ao_samd21_usart0;
+
+char
+ao_serial0_getchar(void);
+
+int
+_ao_serial0_pollchar(void);
+
+uint8_t
+_ao_serial0_sleep_for(uint16_t timeout);
+
+void
+ao_serial0_putchar(char c);
+
+void
+ao_serial0_drain(void);
+
+void
+ao_serial0_set_speed(uint8_t speed);
+#endif
+
+#if HAS_SERIAL_1
+extern struct ao_samd21_usart  ao_samd21_usart1;
+
+char
+ao_serial1_getchar(void);
+
+int
+_ao_serial1_pollchar(void);
+
+uint8_t
+_ao_serial1_sleep_for(uint16_t timeout);
+
+void
+ao_serial1_putchar(char c);
+
+void
+ao_serial1_drain(void);
+
+void
+ao_serial1_set_speed(uint8_t speed);
+#endif
+
+#if HAS_SERIAL_2
+extern struct ao_samd21_usart  ao_samd21_usart2;
+
+char
+ao_serial2_getchar(void);
+
+int
+_ao_serial2_pollchar(void);
+
+uint8_t
+_ao_serial2_sleep_for(uint16_t timeout);
+
+void
+ao_serial2_putchar(char c);
+
+void
+ao_serial2_drain(void);
+
+void
+ao_serial2_set_speed(uint8_t speed);
+#endif
+
+#if HAS_SERIAL_3
+extern struct ao_samd21_usart  ao_samd21_usart3;
+
+char
+ao_serial3_getchar(void);
+
+int
+_ao_serial3_pollchar(void);
+
+uint8_t
+_ao_serial3_sleep_for(uint16_t timeout);
+
+void
+ao_serial3_putchar(char c);
+
+void
+ao_serial3_drain(void);
+
+void
+ao_serial3_set_speed(uint8_t speed);
+#endif
+
+#if HAS_SERIAL_4
+extern struct ao_samd21_usart  ao_samd21_usart4;
+
+char
+ao_serial4_getchar(void);
+
+int
+_ao_serial4_pollchar(void);
+
+uint8_t
+_ao_serial4_sleep_for(uint16_t timeout);
+
+void
+ao_serial4_putchar(char c);
+
+void
+ao_serial4_drain(void);
+
+void
+ao_serial4_set_speed(uint8_t speed);
+#endif
+
+#if HAS_SERIAL_5
+extern struct ao_samd21_usart  ao_samd21_usart5;
+
+char
+ao_serial5_getchar(void);
+
+int
+_ao_serial5_pollchar(void);
+
+uint8_t
+_ao_serial5_sleep_for(uint16_t timeout);
+
+void
+ao_serial5_putchar(char c);
+
+void
+ao_serial5_drain(void);
+
+void
+ao_serial5_set_speed(uint8_t speed);
+#endif
+
+void
+ao_serial_init(void);
+
+#endif /* _AO_SERIAL_H_ */
diff --git a/src/samd21/ao_serial_samd21.c b/src/samd21/ao_serial_samd21.c
new file mode 100644 (file)
index 0000000..97d8379
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Copyright © 2019 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.
+ */
+
+#include <ao.h>
+
+static int
+_ao_usart_tx_start(struct ao_samd21_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, foo) == 1) {
+                       ao_exti_enable(usart->gpio_cts, usart->pin_cts);
+                       return 0;
+               }
+#endif
+               if (usart->reg->intflag & (1 << SAMD21_SERCOM_INTFLAG_DRE))
+               {
+                       usart->tx_running = 1;
+                       usart->reg->intenset = (1 << SAMD21_SERCOM_INTFLAG_DRE) | (1 << SAMD21_SERCOM_INTFLAG_TXC);
+                       ao_fifo_remove(usart->tx_fifo, usart->reg->data);
+                       ao_wakeup(&usart->tx_fifo);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static void
+_ao_usart_rx(struct ao_samd21_usart *usart, int is_stdin)
+{
+       if (usart->reg->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) {
+               uint8_t data = (uint8_t) usart->reg->data;
+               if (!ao_fifo_full(usart->rx_fifo)) {
+                       ao_fifo_insert(usart->rx_fifo, data);
+                       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, usart->pin_rts, 1);
+                               usart->rts = 0;
+                       }
+#endif
+               }
+       }
+}
+
+static void
+ao_usart_isr(struct ao_samd21_usart *usart, int is_stdin)
+{
+       _ao_usart_rx(usart, is_stdin);
+
+       if (!_ao_usart_tx_start(usart))
+               usart->reg->intenclr = (1 << SAMD21_SERCOM_INTFLAG_DRE);
+
+       if (usart->reg->intflag & (1 << SAMD21_SERCOM_INTFLAG_TXC)) {
+               usart->tx_running = 0;
+               usart->reg->intenclr = (1 << SAMD21_SERCOM_INTFLAG_TXC);
+               if (usart->draining) {
+                       usart->draining = 0;
+                       ao_wakeup(&usart->tx_fifo);
+               }
+       }
+}
+
+static const uint32_t 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_samd21_usart *usart, uint8_t speed)
+{
+       struct samd21_sercom *reg = usart->reg;
+       uint64_t        top = (uint64_t) ao_usart_speeds[speed] << (4 + 16);
+       uint16_t        baud = (uint16_t) (65536 - (top + AO_SYSCLK/2) / AO_SYSCLK);
+       uint32_t        ctrla = reg->ctrla;
+
+       if (ctrla & (1UL << SAMD21_SERCOM_CTRLA_ENABLE)) {
+               usart->reg->ctrla = ctrla & ~(1UL << SAMD21_SERCOM_CTRLA_ENABLE);
+               while (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
+                       ;
+       }
+       usart->reg->baud = baud;
+       if (ctrla & (1UL << SAMD21_SERCOM_CTRLA_ENABLE)) {
+               usart->reg->ctrla = ctrla;
+               while (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
+                       ;
+       }
+}
+
+static void
+ao_usart_init(struct ao_samd21_usart *usart, bool hw_flow, uint8_t id, uint8_t txpo, uint8_t rxpo)
+{
+       struct samd21_sercom *reg = usart->reg;
+
+       (void) hw_flow;
+
+       /* Send a clock along */
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_SERCOM0_CORE + id);
+
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_SERCOM0_POS + id);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_SERCOM0_POS + id, 4);
+
+       /* enable */
+       samd21_pm.apbcmask |= (1 << (SAMD21_PM_APBCMASK_SERCOM0 + id));
+
+       /* Reset */
+       reg->ctrla = (1 << SAMD21_SERCOM_CTRLA_SWRST);
+
+       while ((reg->ctrla & (1 << SAMD21_SERCOM_CTRLA_SWRST)) ||
+              (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_SWRST)))
+               ;
+
+       reg->ctrlb = ((0 << SAMD21_SERCOM_CTRLB_CHSIZE) |
+                     (0 << SAMD21_SERCOM_CTRLB_SBMODE) |
+                     (0 << SAMD21_SERCOM_CTRLB_COLDEN) |
+                     (0 << SAMD21_SERCOM_CTRLB_SFDE) |
+                     (0 << SAMD21_SERCOM_CTRLB_ENC) |
+                     (0 << SAMD21_SERCOM_CTRLB_PMODE) |
+                     (1 << SAMD21_SERCOM_CTRLB_TXEN) |
+                     (1 << SAMD21_SERCOM_CTRLB_RXEN) |
+                     (3 << SAMD21_SERCOM_CTRLB_FIFOCLR));
+
+       ao_usart_set_speed(usart, AO_SERIAL_SPEED_9600);
+
+       /* finish setup and enable the hardware */
+       reg->ctrla = ((0 << SAMD21_SERCOM_CTRLA_SWRST) |
+                     (1 << SAMD21_SERCOM_CTRLA_ENABLE) |
+                     (1 << SAMD21_SERCOM_CTRLA_MODE) |
+                     (1 << SAMD21_SERCOM_CTRLA_RUNSTDBY) |
+                     (0 << SAMD21_SERCOM_CTRLA_IBON) |
+                     (0 << SAMD21_SERCOM_CTRLA_SAMPR) |
+                     (txpo << SAMD21_SERCOM_CTRLA_TXPO) |      /* pad[2] */
+                     (rxpo << SAMD21_SERCOM_CTRLA_RXPO) |      /* pad[3] */
+                     (0 << SAMD21_SERCOM_CTRLA_SAMPA) |
+                     (0 << SAMD21_SERCOM_CTRLA_FORM) | /* no parity */
+                     (0 << SAMD21_SERCOM_CTRLA_CMODE) | /* async */
+                     (0 << SAMD21_SERCOM_CTRLA_CPOL) |
+                     (1 << SAMD21_SERCOM_CTRLA_DORD)); /* LSB first */
+
+       /* Enable receive interrupt */
+       reg->intenset = (1 << SAMD21_SERCOM_INTFLAG_RXC);
+
+       while (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
+               ;
+
+}
+
+static int
+_ao_usart_pollchar(struct ao_samd21_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 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, foo, 0);
+                       usart->rts = 1;
+               }
+#endif
+               c = u;
+       }
+       return c;
+}
+
+static char
+ao_usart_getchar(struct ao_samd21_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 void
+ao_usart_putchar(struct ao_samd21_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_samd21_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();
+}
+
+#if HAS_SERIAL_0
+
+struct ao_samd21_usart ao_samd21_usart0;
+
+void samd21_sercom0_isr(void) { ao_usart_isr(&ao_samd21_usart0, USE_SERIAL_0_STDIN); }
+
+char
+ao_serial0_getchar(void)
+{
+       return ao_usart_getchar(&ao_samd21_usart0);
+}
+
+void
+ao_serial0_putchar(char c)
+{
+       ao_usart_putchar(&ao_samd21_usart0, c);
+}
+
+int
+_ao_serial0_pollchar(void)
+{
+       return _ao_usart_pollchar(&ao_samd21_usart0);
+}
+
+void
+ao_serial0_drain(void)
+{
+       ao_usart_drain(&ao_samd21_usart0);
+}
+
+void
+ao_serial0_set_speed(uint8_t speed)
+{
+       ao_usart_drain(&ao_samd21_usart0);
+       ao_usart_set_speed(&ao_samd21_usart0, speed);
+}
+#endif /* HAS_SERIAL_0 */
+
+#if HAS_SERIAL_1
+
+struct ao_samd21_usart ao_samd21_usart1;
+
+void samd21_sercom1_isr(void) { ao_usart_isr(&ao_samd21_usart1, USE_SERIAL_1_STDIN); }
+
+char
+ao_serial1_getchar(void)
+{
+       return ao_usart_getchar(&ao_samd21_usart1);
+}
+
+void
+ao_serial1_putchar(char c)
+{
+       ao_usart_putchar(&ao_samd21_usart1, c);
+}
+
+int
+_ao_serial1_pollchar(void)
+{
+       return _ao_usart_pollchar(&ao_samd21_usart1);
+}
+
+void
+ao_serial1_drain(void)
+{
+       ao_usart_drain(&ao_samd21_usart1);
+}
+
+void
+ao_serial1_set_speed(uint8_t speed)
+{
+       ao_usart_drain(&ao_samd21_usart1);
+       ao_usart_set_speed(&ao_samd21_usart1, speed);
+}
+#endif /* HAS_SERIAL_1 */
+
+void
+ao_serial_init(void)
+{
+       uint8_t txpo, rxpo;
+#if HAS_SERIAL_0
+
+#if SERIAL_0_PA10_PA11
+       /* Pin settings */
+       ao_enable_port(&samd21_port_a);
+       samd21_port_pmux_set(&samd21_port_a, 10, SAMD21_PORT_PMUX_FUNC_C);
+       samd21_port_pmux_set(&samd21_port_a, 11, SAMD21_PORT_PMUX_FUNC_C);
+       txpo = SAMD21_SERCOM_CTRLA_TXPO_TX_2; /* pad 2 */
+       rxpo = SAMD21_SERCOM_CTRLA_RXPO_RX_3; /* pad 3 */
+#elif SERIAL_0_PA08_PA09
+       /* Pin settings */
+       ao_enable_port(&samd21_port_a);
+       samd21_port_pmux_set(&samd21_port_a, 8, SAMD21_PORT_PMUX_FUNC_C);
+       samd21_port_pmux_set(&samd21_port_a, 9, SAMD21_PORT_PMUX_FUNC_C);
+       txpo = SAMD21_SERCOM_CTRLA_TXPO_TX_0; /* pad 0 */
+       rxpo = SAMD21_SERCOM_CTRLA_RXPO_RX_1; /* pad 1 */
+#else
+#error "No SERIAL_0 port configuration specified"
+#endif
+
+       ao_samd21_usart0.reg = &samd21_sercom0;
+       ao_usart_init(&ao_samd21_usart0, 0, 0, txpo, rxpo);
+
+#if USE_SERIAL_0_STDIN
+       ao_add_stdio(_ao_serial0_pollchar,
+                    ao_serial0_putchar,
+                    NULL);
+#endif
+#endif
+#if HAS_SERIAL_1
+
+#if SERIAL_1_PA00_PA01
+       /* Pin settings */
+       ao_enable_port(&samd21_port_a);
+       samd21_port_pmux_set(&samd21_port_a, 0, SAMD21_PORT_PMUX_FUNC_D);
+       samd21_port_pmux_set(&samd21_port_a, 1, SAMD21_PORT_PMUX_FUNC_D);
+       txpo = SAMD21_SERCOM_CTRLA_TXPO_TX_0;
+       rxpo = SAMD21_SERCOM_CTRLA_RXPO_RX_1;
+#else
+#error "No SERIAL_1 port configuration specified"
+#endif
+
+       ao_samd21_usart1.reg = &samd21_sercom1;
+       ao_usart_init(&ao_samd21_usart1, 0, 1, txpo, rxpo);
+
+#if USE_SERIAL_1_STDIN
+       ao_add_stdio(_ao_serial1_pollchar,
+                    ao_serial1_putchar,
+                    NULL);
+#endif
+#endif
+}
diff --git a/src/samd21/ao_spi_samd21.c b/src/samd21/ao_spi_samd21.c
new file mode 100644 (file)
index 0000000..aee8cb1
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+ * 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.
+ */
+
+#include <ao.h>
+#include <ao_dma_samd21.h>
+
+static uint8_t         ao_spi_mutex[SAMD21_NUM_SERCOM];
+static uint16_t                ao_spi_pin_config[SAMD21_NUM_SERCOM];
+
+#define SPI_DEBUG      0
+#define SPI_USE_DMA    1
+
+struct ao_spi_samd21_info {
+       struct samd21_sercom    *sercom;
+};
+
+static const struct ao_spi_samd21_info ao_spi_samd21_info[SAMD21_NUM_SERCOM] = {
+       {
+               .sercom = &samd21_sercom0,
+       },
+       {
+               .sercom = &samd21_sercom1,
+       },
+       {
+               .sercom = &samd21_sercom2,
+       },
+       {
+               .sercom = &samd21_sercom3,
+       },
+       {
+               .sercom = &samd21_sercom4,
+       },
+       {
+               .sercom = &samd21_sercom5,
+       },
+};
+
+static uint8_t spi_dev_null;
+
+#if SPI_USE_DMA
+
+static uint8_t ao_spi_done[SAMD21_NUM_SERCOM];
+
+static void
+_ao_spi_recv_dma_done(uint8_t dma_id, void *closure)
+{
+       uint8_t id = (uint8_t) (uintptr_t) closure;
+
+       (void) dma_id;
+       ao_spi_done[id] = 1;
+       ao_wakeup(&ao_spi_done[id]);
+}
+
+static inline uint32_t
+dma_chctrlb(uint8_t id, bool tx)
+{
+       uint32_t        chctrlb = 0;
+
+       /* No complicated actions needed */
+       chctrlb |= SAMD21_DMAC_CHCTRLB_CMD_NOACT << SAMD21_DMAC_CHCTRLB_CMD;
+
+       /* Trigger after each byte transferred */
+       chctrlb |= SAMD21_DMAC_CHCTRLB_TRIGACT_BEAT << SAMD21_DMAC_CHCTRLB_TRIGACT;
+
+       /* Set the trigger source */
+       if (tx)
+               chctrlb |= SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_TX(id) << SAMD21_DMAC_CHCTRLB_TRIGSRC;
+       else
+               chctrlb |= SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_RX(id) << SAMD21_DMAC_CHCTRLB_TRIGSRC;
+
+       /* RX has priority over TX so that we don't drop incoming bytes */
+       if (tx)
+               chctrlb |= SAMD21_DMAC_CHCTRLB_LVL_LVL0 << SAMD21_DMAC_CHCTRLB_LVL;
+       else
+               chctrlb |= SAMD21_DMAC_CHCTRLB_LVL_LVL3 << SAMD21_DMAC_CHCTRLB_LVL;
+
+       /* No events needed */
+       chctrlb |= 0UL << SAMD21_DMAC_CHCTRLB_EVOE;
+       chctrlb |= 0UL << SAMD21_DMAC_CHCTRLB_EVIE;
+
+       /* And no actions either */
+       chctrlb |= SAMD21_DMAC_CHCTRLB_EVACT_NOACT << SAMD21_DMAC_CHCTRLB_EVACT;
+
+       return chctrlb;
+}
+
+static inline uint16_t
+dma_btctrl(bool step, bool tx)
+{
+       uint16_t        btctrl = 0;
+
+       /* Always step by 1 */
+       btctrl |= SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X1 << SAMD21_DMAC_DESC_BTCTRL_STEPSIZE;
+
+       /* Step the source if transmit, otherwise step the dest */
+       if (tx)
+               btctrl |= SAMD21_DMAC_DESC_BTCTRL_STEPSEL_SRC << SAMD21_DMAC_DESC_BTCTRL_STEPSEL;
+       else
+               btctrl |= SAMD21_DMAC_DESC_BTCTRL_STEPSEL_DST << SAMD21_DMAC_DESC_BTCTRL_STEPSEL;
+
+       /* Set the increment if stepping */
+       if (tx) {
+               if (step)
+                       btctrl |= 1UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC;
+               else
+                       btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC;
+               btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC;
+       } else {
+               btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC;
+               if (step)
+                       btctrl |= 1UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC;
+               else
+                       btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC;
+       }
+
+       /* byte at a time please */
+       btctrl |= SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_BYTE << SAMD21_DMAC_DESC_BTCTRL_BEATSIZE;
+
+       /*
+        * Watch for interrupts on RX -- we need to wait for the last byte to get received
+        * to know the SPI bus is idle
+        */
+       if (tx)
+               btctrl |= SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_NOACT << SAMD21_DMAC_DESC_BTCTRL_BLOCKACT;
+       else
+               btctrl |= SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_INT << SAMD21_DMAC_DESC_BTCTRL_BLOCKACT;
+
+       /* don't need any events */
+       btctrl |= SAMD21_DMAC_DESC_BTCTRL_EVOSEL_DISABLE << SAMD21_DMAC_DESC_BTCTRL_EVOSEL;
+
+       /* And make the descriptor valid */
+       btctrl |= 1UL << SAMD21_DMAC_DESC_BTCTRL_VALID;
+
+       return btctrl;
+}
+
+static void
+spi_run(const void *out, void *in, uint16_t len, uint16_t spi_index, bool step_out, bool step_in)
+{
+       const uint8_t           *o = out;
+       uint8_t                 *i = in;
+       uint8_t                 id = AO_SPI_INDEX(spi_index);
+       struct samd21_sercom    *sercom = ao_spi_samd21_info[id].sercom;
+
+       ao_arch_block_interrupts();
+       ao_spi_done[id] = 0;
+
+       /*
+        * Stepped addresses to the DMA engine point past the end of
+        * the block
+        */
+       if (step_out)
+               o += len;
+       if (step_in)
+               i += len;
+
+       /* read any stuck data */
+       (void) sercom->data;
+
+       _ao_dma_start_transfer(AO_SERCOM_INPUT_DMA_ID(id),
+                              (void *) &sercom->data,
+                              i,
+                              len,
+                              dma_chctrlb(id, false),
+                              dma_btctrl(step_in, false),
+
+                              _ao_spi_recv_dma_done,
+                              (void *) (uintptr_t) id
+               );
+
+       _ao_dma_start_transfer(AO_SERCOM_OUTPUT_DMA_ID(id),
+                              o,
+                              (void *) &sercom->data,
+                              len,
+                              dma_chctrlb(id, true),
+                              dma_btctrl(step_out, true),
+                              NULL,
+                              NULL
+               );
+
+       while (ao_spi_done[id] == 0)
+               ao_sleep(&ao_spi_done[id]);
+
+       _ao_dma_done_transfer(AO_SERCOM_OUTPUT_DMA_ID(id));
+       _ao_dma_done_transfer(AO_SERCOM_INPUT_DMA_ID(id));
+       ao_arch_release_interrupts();
+}
+
+#else
+
+static void
+spi_run(const void *out, void *in, uint16_t len, uint16_t spi_index, bool step_out, bool step_in)
+{
+       uint8_t                 id = AO_SPI_INDEX(spi_index);
+       struct samd21_sercom    *sercom = ao_spi_samd21_info[id].sercom;
+       const uint8_t           *o = out;
+       uint8_t                 *i = in;
+
+       while (len--) {
+#if SPI_DEBUG
+               printf("%02x", *o);
+#endif
+               sercom->data = *o;
+               while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0)
+                       ;
+               *i = (uint8_t) sercom->data;
+#if SPI_DEBUG
+               printf("\t%02x\n", *i);
+#endif
+               if (step_out)
+                       o++;
+               if (step_in)
+                       i++;
+       }
+}
+
+#endif
+
+void
+ao_spi_send(const void *block, uint16_t len, uint16_t spi_index)
+{
+       spi_run(block, &spi_dev_null, len, spi_index, true, false);
+}
+
+void
+ao_spi_send_fixed(uint8_t data, uint16_t len, uint16_t spi_index)
+{
+       spi_run(&data, &spi_dev_null, len, spi_index, false, false);
+}
+
+void
+ao_spi_recv(void *block, uint16_t len, uint16_t spi_index)
+{
+       spi_dev_null = 0xff;
+       spi_run(&spi_dev_null, block, len, spi_index, false, true);
+}
+
+
+void
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint16_t spi_index)
+{
+       spi_run(out, in, len, spi_index, true, true);
+}
+
+static void
+ao_spi_disable_pin_config(uint16_t spi_pin_config)
+{
+       switch (spi_pin_config) {
+#if HAS_SPI_0
+       case AO_SPI_PIN_CONFIG(AO_SPI_0_PA08_PA09_PA10):
+               samd21_port_pmux_clr(&samd21_port_a, 8);        /* MOSI */
+               samd21_port_pmux_clr(&samd21_port_a, 9);        /* SCLK */
+               samd21_port_pmux_clr(&samd21_port_a, 10);       /* MISO */
+               break;
+       case AO_SPI_PIN_CONFIG(AO_SPI_0_PA04_PA05_PA06):
+               samd21_port_pmux_clr(&samd21_port_a, 4);        /* MOSI */
+               samd21_port_pmux_clr(&samd21_port_a, 5);        /* SCLK */
+               samd21_port_pmux_clr(&samd21_port_a, 6);        /* MISO */
+               break;
+#endif
+#if HAS_SPI_3
+       case AO_SPI_PIN_CONFIG(AO_SPI_3_PA22_PA23_PA20):
+               samd21_port_pmux_clr(&samd21_port_a, 22);       /* MOSI */
+               samd21_port_pmux_clr(&samd21_port_a, 23);       /* SCLK */
+               samd21_port_pmux_clr(&samd21_port_a, 20);       /* MISO */
+               break;
+#endif
+#if HAS_SPI_4
+       case AO_SPI_PIN_CONFIG(AO_SPI_4_PB10_PB11_PA12):
+               samd21_port_pmux_clr(&samd21_port_b, 10);       /* MOSI */
+               samd21_port_pmux_clr(&samd21_port_b, 11);       /* SCLK */
+               samd21_port_pmux_clr(&samd21_port_a, 12);       /* MISO */
+               break;
+#endif
+#if HAS_SPI_5
+       case AO_SPI_PIN_CONFIG(AO_SPI_5_PB22_PB23_PB03):
+               samd21_port_pmux_clr(&samd21_port_b, 22);       /* MOSI */
+               samd21_port_pmux_clr(&samd21_port_b, 23);       /* SCLK */
+               samd21_port_pmux_clr(&samd21_port_b, 3);        /* MISO */
+               break;
+#endif
+       case 0xffff:
+               break;
+       }
+}
+
+static void
+ao_spi_enable_pin_config(uint16_t spi_pin_config)
+{
+       switch (spi_pin_config) {
+#if HAS_SPI_0
+       case AO_SPI_PIN_CONFIG(AO_SPI_0_PA08_PA09_PA10):
+               ao_enable_output(&samd21_port_a, 8, 1);
+               ao_enable_output(&samd21_port_a, 9, 1);
+               ao_enable_input(&samd21_port_a, 10, AO_MODE_PULL_NONE);
+
+               samd21_port_pmux_set(&samd21_port_a, 8, SAMD21_PORT_PMUX_FUNC_C);       /* MOSI */
+               samd21_port_pmux_set(&samd21_port_a, 9, SAMD21_PORT_PMUX_FUNC_C);       /* SCLK */
+               samd21_port_pmux_set(&samd21_port_a, 10, SAMD21_PORT_PMUX_FUNC_C);      /* MISO */
+               break;
+       case AO_SPI_PIN_CONFIG(AO_SPI_0_PA04_PA05_PA06):
+               ao_enable_output(&samd21_port_a, 4, 1);
+               ao_enable_output(&samd21_port_a, 5, 1);
+               ao_enable_input(&samd21_port_a, 6, AO_MODE_PULL_NONE);
+
+               samd21_port_pmux_set(&samd21_port_a, 4, SAMD21_PORT_PMUX_FUNC_D);       /* MOSI */
+               samd21_port_pmux_set(&samd21_port_a, 5, SAMD21_PORT_PMUX_FUNC_D);       /* SCLK */
+               samd21_port_pmux_set(&samd21_port_a, 6, SAMD21_PORT_PMUX_FUNC_D);       /* MISO */
+               break;
+#endif
+#if HAS_SPI_3
+       case AO_SPI_PIN_CONFIG(AO_SPI_3_PA22_PA23_PA20):
+               ao_enable_output(&samd21_port_a, 22, 1);
+               ao_enable_output(&samd21_port_a, 23, 1);
+               ao_enable_input(&samd21_port_a, 20, AO_MODE_PULL_NONE);
+
+               samd21_port_pmux_set(&samd21_port_a, 22, SAMD21_PORT_PMUX_FUNC_C);      /* MOSI */
+               samd21_port_pmux_set(&samd21_port_a, 23, SAMD21_PORT_PMUX_FUNC_C);      /* SCLK */
+               samd21_port_pmux_set(&samd21_port_a, 20, SAMD21_PORT_PMUX_FUNC_D);      /* MISO */
+               break;
+#endif
+#if HAS_SPI_4
+       case AO_SPI_PIN_CONFIG(AO_SPI_4_PB10_PB11_PA12):
+               ao_enable_output(&samd21_port_b, 10, 1);
+               ao_enable_output(&samd21_port_b, 11, 1);
+               ao_enable_input(&samd21_port_a, 12, AO_MODE_PULL_NONE);
+
+               samd21_port_pmux_set(&samd21_port_b, 10, SAMD21_PORT_PMUX_FUNC_D);      /* MOSI */
+               samd21_port_pmux_set(&samd21_port_b, 11, SAMD21_PORT_PMUX_FUNC_D);      /* SCLK */
+               samd21_port_pmux_set(&samd21_port_a, 12, SAMD21_PORT_PMUX_FUNC_D);      /* MISO */
+               break;
+#endif
+#if HAS_SPI_5
+       case AO_SPI_PIN_CONFIG(AO_SPI_5_PB22_PB23_PB03):
+               ao_enable_output(&samd21_port_b, 22, 1);
+               ao_enable_output(&samd21_port_b, 23, 1);
+               ao_enable_input(&samd21_port_b, 3, AO_MODE_PULL_NONE);
+
+               samd21_port_pmux_set(&samd21_port_b, 22, SAMD21_PORT_PMUX_FUNC_D);      /* 5.2 MOSI */
+               samd21_port_pmux_set(&samd21_port_b, 23, SAMD21_PORT_PMUX_FUNC_D);      /* 5.3 SCLK */
+               samd21_port_pmux_set(&samd21_port_b, 3, SAMD21_PORT_PMUX_FUNC_D);       /* 5.1 MISO */
+               break;
+#endif
+       default:
+               ao_panic(AO_PANIC_SPI);
+               break;
+       }
+}
+
+static void
+ao_spi_config(uint16_t spi_index, uint32_t baud)
+{
+       uint16_t                spi_pin_config = AO_SPI_PIN_CONFIG(spi_index);
+       uint8_t                 id = AO_SPI_INDEX(spi_index);
+       struct samd21_sercom    *sercom = ao_spi_samd21_info[id].sercom;
+
+       if (spi_pin_config != ao_spi_pin_config[id]) {
+               ao_spi_disable_pin_config(ao_spi_pin_config[id]);
+               ao_spi_enable_pin_config(spi_pin_config);
+               ao_spi_pin_config[id] = spi_pin_config;
+       }
+
+       sercom->baud = (uint16_t) baud;
+
+       /* Set spi mode */
+       uint32_t ctrla = sercom->ctrla;
+       ctrla &= ~((1UL << SAMD21_SERCOM_CTRLA_CPOL) |
+                  (1UL << SAMD21_SERCOM_CTRLA_CPHA) |
+                  (SAMD21_SERCOM_CTRLA_DOPO_MASK << SAMD21_SERCOM_CTRLA_DOPO) |
+                  (SAMD21_SERCOM_CTRLA_DIPO_MASK << SAMD21_SERCOM_CTRLA_DIPO));
+       ctrla |= ((AO_SPI_CPOL(spi_index) << SAMD21_SERCOM_CTRLA_CPOL) |
+                 (AO_SPI_CPHA(spi_index) << SAMD21_SERCOM_CTRLA_CPHA) |
+                 (AO_SPI_DOPO(spi_index) << SAMD21_SERCOM_CTRLA_DOPO) |
+                 (AO_SPI_DIPO(spi_index) << SAMD21_SERCOM_CTRLA_DIPO));
+
+       /* finish setup and enable the hardware */
+       ctrla |= (1 << SAMD21_SERCOM_CTRLA_ENABLE);
+
+#if SPI_DEBUG
+       printf("ctrla %08lx\n", ctrla);
+#endif
+
+       sercom->ctrla = ctrla;
+
+       while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
+               ;
+}
+
+void
+ao_spi_get(uint16_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(uint16_t spi_index)
+{
+       uint8_t                 id = AO_SPI_INDEX(spi_index);
+       struct samd21_sercom    *sercom = ao_spi_samd21_info[id].sercom;
+
+       sercom->ctrla &= ~(1UL << SAMD21_SERCOM_CTRLA_ENABLE);
+       while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
+               ;
+       ao_mutex_put(&ao_spi_mutex[id]);
+}
+
+static void
+ao_spi_init_sercom(uint8_t id)
+{
+       struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
+
+       /* Send a clock along */
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_SERCOM0_CORE + id);
+
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_SERCOM0_POS + id);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_SERCOM0_POS + id, 4);
+
+       /* Enable */
+       samd21_pm.apbcmask |= (1 << (SAMD21_PM_APBCMASK_SERCOM0 + id));
+
+       /* Reset */
+       sercom->ctrla = (1 << SAMD21_SERCOM_CTRLA_SWRST);
+
+       while ((sercom->ctrla & (1 << SAMD21_SERCOM_CTRLA_SWRST)) ||
+              (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_SWRST)))
+               ;
+
+       /* set SPI mode */
+       sercom->ctrla = ((SAMD21_SERCOM_CTRLA_DORD_MSB << SAMD21_SERCOM_CTRLA_DORD) |
+                        (0 << SAMD21_SERCOM_CTRLA_CPOL) |
+                        (0 << SAMD21_SERCOM_CTRLA_CPHA) |
+                        (0 << SAMD21_SERCOM_CTRLA_FORM) |
+                        (2 << SAMD21_SERCOM_CTRLA_DIPO) |
+                        (0 << SAMD21_SERCOM_CTRLA_DOPO) |
+                        (0 << SAMD21_SERCOM_CTRLA_IBON) |
+                        (0 << SAMD21_SERCOM_CTRLA_RUNSTDBY) |
+                        (SAMD21_SERCOM_CTRLA_MODE_SPI_HOST << SAMD21_SERCOM_CTRLA_MODE) |
+                        (0 << SAMD21_SERCOM_CTRLA_ENABLE) |
+                        (0 << SAMD21_SERCOM_CTRLA_SWRST));
+
+       sercom->ctrlb = ((1 << SAMD21_SERCOM_CTRLB_RXEN) |
+                        (0 << SAMD21_SERCOM_CTRLB_AMODE) |
+                        (0 << SAMD21_SERCOM_CTRLB_MSSEN) |
+                        (0 << SAMD21_SERCOM_CTRLB_SSDE) |
+                        (0 << SAMD21_SERCOM_CTRLB_PLOADEN) |
+                        (SAMD21_SERCOM_CTRLB_CHSIZE_8 << SAMD21_SERCOM_CTRLB_CHSIZE));
+
+       ao_spi_pin_config[id] = 0xffff;
+}
+
+void
+ao_spi_init(void)
+{
+#if HAS_SPI_0
+       ao_spi_init_sercom(0);
+#endif
+#if HAS_SPI_1
+       ao_spi_init_sercom(1);
+#endif
+#if HAS_SPI_2
+       ao_spi_init_sercom(2);
+#endif
+#if HAS_SPI_3
+       ao_spi_init_sercom(3);
+#endif
+#if HAS_SPI_4
+       ao_spi_init_sercom(4);
+#endif
+#if HAS_SPI_5
+       ao_spi_init_sercom(5);
+#endif
+}
diff --git a/src/samd21/ao_tc_samd21.c b/src/samd21/ao_tc_samd21.c
new file mode 100644 (file)
index 0000000..f08c738
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2019 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.
+ */
+
+#include <ao.h>
+#include <ao_tc-samd21.h>
+
+void
+ao_tc_set(struct samd21_tc *tc, uint8_t channel, uint32_t value)
+{
+       tc->mode_16.cc[channel] = value;
+}
+
+static void
+ao_tc_init(struct samd21_tc *tc, uint32_t apbcmask)
+{
+       samd21_pm.apbcmask |= apbcmask;
+
+       /* Reset the device */
+       tc->ctrla = (1 << SAMD21_TC_CTRLA_SWRST);
+
+       while ((tc->ctrla & (1 << SAMD21_TC_CTRLA_SWRST)) != 0 ||
+              (tc->status & (1 << SAMD21_TC_STATUS_SYNCBUSY)) != 0)
+               ;
+
+       tc->ctrla = ((SAMD21_TC_CTRLA_PRESCSYNC_GCLK << SAMD21_TC_CTRLA_PRESCSYNC) |
+                    (SAMD21_TC_CTRLA_PRESCALER_DIV1 << SAMD21_TC_CTRLA_PRESCALER) |
+                    (SAMD21_TC_CTRLA_WAVEGEN_NPWM << SAMD21_TC_CTRLA_WAVEGEN) |
+                    (SAMD21_TC_CTRLA_MODE_COUNT16) |
+                    (1 << SAMD21_TC_CTRLA_ENABLE));
+       tc->dbgctrl = (1 << SAMD21_TC_DBGCTRL_DBGRUN);
+}
+
+void
+ao_tc_samd21_init(void)
+{
+       /* SAMD21G18 has only TC3-TC5 */
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC2_TC3);
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TC4_TC5);
+
+       ao_tc_init(&samd21_tc3, 1 << SAMD21_PM_APBCMASK_TC3);
+       ao_tc_init(&samd21_tc4, 1 << SAMD21_PM_APBCMASK_TC4);
+       ao_tc_init(&samd21_tc5, 1 << SAMD21_PM_APBCMASK_TC5);
+}
diff --git a/src/samd21/ao_tc_samd21.h b/src/samd21/ao_tc_samd21.h
new file mode 100644 (file)
index 0000000..1d6cf41
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2019 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_TC_SAMD21_H_
+#define _AO_TC_SAMD21_H_
+
+void
+ao_tc_set(struct samd21_tc *tc, uint8_t channel, uint32_t value);
+
+void
+ao_tc_samd21_init(void);
+
+#endif /* _AO_TC_SAMD21_H_ */
diff --git a/src/samd21/ao_tcc_samd21.c b/src/samd21/ao_tcc_samd21.c
new file mode 100644 (file)
index 0000000..9cb45c0
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2019 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.
+ */
+
+#include <ao.h>
+#include <ao_tcc-samd21.h>
+
+void
+ao_tcc_set(struct samd21_tcc *tcc, uint8_t channel, uint32_t value)
+{
+       tcc->cc[channel] = value;
+}
+
+static void
+ao_tcc_init(struct samd21_tcc *tcc, uint32_t apbcmask)
+{
+       samd21_pm.apbcmask |= apbcmask;
+
+       /* Reset the device */
+       tcc->ctrla = (1 << SAMD21_TCC_CTRLA_SWRST);
+
+       while ((tcc->ctrla & (1 << SAMD21_TCC_CTRLA_SWRST)) != 0 ||
+              (tcc->syncbusy & (1 << SAMD21_TCC_SYNCBUSY_SWRST)) != 0)
+               ;
+
+       tcc->per = AO_TCC_PERIOD - 1;
+
+#if 0
+       tcc->evctrl = ((1 << SAMD21_TCC_EVCTRL_MCEO(0)) |
+                      (1 << SAMD21_TCC_EVCTRL_MCEO(1)) |
+                      (1 << SAMD21_TCC_EVCTRL_MCEO(2)) |
+                      (1 << SAMD21_TCC_EVCTRL_MCEO(3)));
+#endif
+
+       tcc->wave = ((SAMD21_TCC_WAVE_WAVEGEN_NPWM << SAMD21_TCC_WAVE_WAVEGEN) |
+                    (0 << SAMD21_TCC_WAVE_RAMP) |
+                    (0 << SAMD21_TCC_WAVE_CIPEREN) |
+                    (0 << SAMD21_TCC_WAVE_CCCEN(0)) |
+                    (0 << SAMD21_TCC_WAVE_CCCEN(1)) |
+                    (0 << SAMD21_TCC_WAVE_CCCEN(2)) |
+                    (0 << SAMD21_TCC_WAVE_CCCEN(3)) |
+                    (0 << SAMD21_TCC_WAVE_POL(0)) |
+                    (0 << SAMD21_TCC_WAVE_POL(1)) |
+                    (0 << SAMD21_TCC_WAVE_POL(1)) |
+                    (0 << SAMD21_TCC_WAVE_POL(3)) |
+                    (0 << SAMD21_TCC_WAVE_SWAP(0)) |
+                    (0 << SAMD21_TCC_WAVE_SWAP(1)) |
+                    (0 << SAMD21_TCC_WAVE_SWAP(1)) |
+                    (0 << SAMD21_TCC_WAVE_SWAP(3)));
+
+       tcc->ctrla = (1 << SAMD21_TCC_CTRLA_ENABLE);
+       tcc->dbgctrl = (1 << SAMD21_TCC_DBGCTRL_DBGRUN);
+}
+
+void
+ao_tcc_samd21_init(void)
+{
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC0_TCC1);
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC2_TC3);
+
+       ao_tcc_init(&samd21_tcc0, 1 << SAMD21_PM_APBCMASK_TCC0);
+       ao_tcc_init(&samd21_tcc1, 1 << SAMD21_PM_APBCMASK_TCC1);
+       ao_tcc_init(&samd21_tcc2, 1 << SAMD21_PM_APBCMASK_TCC2);
+}
diff --git a/src/samd21/ao_tcc_samd21.h b/src/samd21/ao_tcc_samd21.h
new file mode 100644 (file)
index 0000000..a8f41a8
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2019 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_TCC_SAMD21_H_
+#define _AO_TCC_SAMD21_H_
+
+void
+ao_tcc_set(struct samd21_tcc *tcc, uint8_t channel, uint32_t value);
+
+void
+ao_tcc_samd21_init(void);
+
+#endif /* _AO_TCC_SAMD21_H_ */
diff --git a/src/samd21/ao_timer.c b/src/samd21/ao_timer.c
new file mode 100644 (file)
index 0000000..2b431d3
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright © 2019 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.
+ */
+
+#include <ao.h>
+
+#ifndef HAS_TICK
+#define HAS_TICK 1
+#endif
+
+#if HAS_TICK
+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        cvr;
+
+       do {
+               before = ao_tick_count;
+               cvr = samd21_systick.cvr;
+               after = ao_tick_count;
+       } while (before != after);
+
+       return (uint64_t) after * (1000000000ULL / AO_HERTZ) +
+               (uint64_t) cvr * (1000000000ULL / AO_SYSTICK);
+}
+
+#if AO_DATA_ALL
+volatile uint8_t       ao_data_interval = 1;
+volatile uint8_t       ao_data_count;
+#endif
+
+void samd21_systick_isr(void)
+{
+       ao_arch_release_interrupts();
+       if (samd21_systick.csr & (1 << SAMD21_SYSTICK_CSR_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 / AO_HERTZ - 1)
+
+void
+ao_timer_init(void)
+{
+       samd21_systick.csr = 0;
+       samd21_systick.rvr = SYSTICK_RELOAD;
+       samd21_systick.cvr = 0;
+       samd21_systick.csr = ((1 << SAMD21_SYSTICK_CSR_ENABLE) |
+                          (1 << SAMD21_SYSTICK_CSR_TICKINT) |
+                          (SAMD21_SYSTICK_CSR_CLKSOURCE_HCLK_8 << SAMD21_SYSTICK_CSR_CLKSOURCE));
+       /* Set clock to lowest priority */
+       samd21_scb.shpr3 |= 3UL << 30;
+}
+
+#endif
+
+
+void
+ao_clock_init(void)
+{
+       /* Set flash wait state to tolerate 48MHz */
+       samd21_nvmctrl.ctrlb |= (1 << SAMD21_NVMCTRL_CTRLB_RWS);
+
+       samd21_pm.apbamask |= ((1 << SAMD21_PM_APBAMASK_GCLK) |
+                              (1 << SAMD21_PM_APBAMASK_SYSCTRL));
+
+       /* Reset gclk */
+       samd21_gclk.ctrl = (1 << SAMD21_GCLK_CTRL_SWRST)
+               ;
+
+       /* Wait for reset to complete */
+       while ((samd21_gclk.ctrl & (1 << SAMD21_GCLK_CTRL_SWRST)) &&
+              (samd21_gclk.status & (1 << SAMD21_GCLK_STATUS_SYNCBUSY)))
+               ;
+
+#ifdef AO_XOSC
+       /* Enable xosc (external xtal oscillator) */
+       samd21_sysctrl.xosc = ((SAMD21_SYSCTRL_XOSC_STARTUP_8192 << SAMD21_SYSCTRL_XOSC_STARTUP) |
+                              (0 << SAMD21_SYSCTRL_XOSC_AMPGC) |
+                              (SAMD21_SYSCTRL_XOSC_GAIN_16MHz << SAMD21_SYSCTRL_XOSC_GAIN) |
+                              (0 << SAMD21_SYSCTRL_XOSC_ONDEMAND) |
+                              (1 << SAMD21_SYSCTRL_XOSC_RUNSTDBY) |
+                              (1 << SAMD21_SYSCTRL_XOSC_XTALEN));
+       samd21_sysctrl.xosc |= ((1 << SAMD21_SYSCTRL_XOSC_ENABLE));
+
+       /* Wait for xosc */
+       while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_XOSCRDY)) == 0)
+               ;
+
+       /* program DPLL */
+
+       /* Divide down */
+       samd21_sysctrl.dpllctrlb = (((AO_XOSC_DIV/2 - 1) << SAMD21_SYSCTRL_DPLLCTRLB_DIV) |
+                                   (0 << SAMD21_SYSCTRL_DPLLCTRLB_LBYPASS) |
+                                   (SAMD21_SYSCTRL_DPLLCTRLB_LTIME_DEFAULT << SAMD21_SYSCTRL_DPLLCTRLB_LTIME) |
+                                   (SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_XOSC << SAMD21_SYSCTRL_DPLLCTRLB_REFCLK) |
+                                   (0 << SAMD21_SYSCTRL_DPLLCTRLB_WUF) |
+                                   (1 << SAMD21_SYSCTRL_DPLLCTRLB_LPEN) |
+                                   (SAMD21_SYSCTRL_DPLLCTRLB_FILTER_DEFAULT << SAMD21_SYSCTRL_DPLLCTRLB_FILTER));
+
+       /* Multiply up */
+       samd21_sysctrl.dpllratio = ((AO_XOSC_MUL - 1) << SAMD21_SYSCTRL_DPLLRATIO_LDR);
+
+       /* Always on in run mode, off in standby mode */
+       samd21_sysctrl.dpllctrla = ((0 << SAMD21_SYSCTRL_DPLLCTRLA_ONDEMAND) |
+                                   (0 << SAMD21_SYSCTRL_DPLLCTRLA_RUNSTDBY));
+
+       /* Enable DPLL */
+       samd21_sysctrl.dpllctrla |= (1 << SAMD21_SYSCTRL_DPLLCTRLA_ENABLE);
+
+       /* Wait for the DPLL to be enabled */
+       while ((samd21_sysctrl.dpllstatus & (1 << SAMD21_SYSCTRL_DPLLSTATUS_ENABLE)) == 0)
+               ;
+
+       /* Wait for the DPLL to be ready */
+       while ((samd21_sysctrl.dpllstatus & (1 << SAMD21_SYSCTRL_DPLLSTATUS_CLKRDY)) == 0)
+               ;
+
+       samd21_gclk_wait_sync();
+
+       /*
+        * Switch generator 0 (CPU clock) to DPLL
+        */
+
+       /* divide by 1 */
+       samd21_gclk_gendiv(AO_GCLK_SYSCLK, AO_XOSC_GCLK_DIV);
+
+       /* select DPLL as source */
+       samd21_gclk_genctrl(SAMD21_GCLK_GENCTRL_SRC_FDPLL96M, AO_GCLK_FDPLL96M);
+#endif
+
+#ifdef AO_DFLL48M
+
+       /*
+        * Enable DFLL48M clock
+        */
+
+       samd21_sysctrl.dfllctrl = (1 << SAMD21_SYSCTRL_DFLLCTRL_ENABLE);
+       samd21_dfll_wait_sync();
+
+#ifdef AO_XOSC32K
+#define AO_GCLK_XOSC32K        1
+
+       /* Enable xosc32k (external 32.768kHz oscillator) */
+       samd21_sysctrl.xosc32k = ((6 << SAMD21_SYSCTRL_XOSC32K_STARTUP) |
+                                 (1 << SAMD21_SYSCTRL_XOSC32K_XTALEN) |
+                                 (1 << SAMD21_SYSCTRL_XOSC32K_EN32K));
+
+       /* requires separate store */
+       samd21_sysctrl.xosc32k |= (1 << SAMD21_SYSCTRL_XOSC32K_ENABLE);
+
+       /* Wait for osc */
+       while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_XOSC32KRDY)) == 0)
+               ;
+
+       /*
+        * Use xosc32k as source of gclk generator AO_GCLK_XOSC32K
+        */
+
+       samd21_gclk_gendiv(AO_GCLK_XOSC32K, 1);
+       samd21_gclk_genctrl(SAMD21_GCLK_GENCTRL_SRC_XOSC32K, AO_GCLK_XOSC32K);
+
+       /*
+        * Use generator as source for dfm48m reference
+        */
+
+       samd21_gclk_clkctrl(AO_GCLK_XOSC32K, SAMD21_GCLK_CLKCTRL_ID_DFLL48M_REF);
+
+       /* Set multiplier to get as close to 48MHz as we can without going over */
+       samd21_sysctrl.dfllmul = (((31/4) << SAMD21_SYSCTRL_DFLLMUL_CSTEP) |
+                                 ((255/4) << SAMD21_SYSCTRL_DFLLMUL_FSTEP) |
+                                 ((AO_DFLL48M / AO_XOSC32K) << SAMD21_SYSCTRL_DFLLMUL_MUL));
+
+       /* pull out coarse calibration value from rom */
+       uint32_t coarse = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL) &
+                          SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL_MASK);
+
+       samd21_sysctrl.dfllval = ((coarse << SAMD21_SYSCTRL_DFLLVAL_COARSE) |
+                                 (512 << SAMD21_SYSCTRL_DFLLVAL_FINE));
+
+       samd21_sysctrl.dfllctrl = 0;
+       samd21_dfll_wait_sync();
+
+       samd21_sysctrl.dfllctrl = ((1 << SAMD21_SYSCTRL_DFLLCTRL_MODE) |
+                                  (1 << SAMD21_SYSCTRL_DFLLCTRL_ENABLE));
+
+       samd21_dfll_wait_sync();
+       samd21_gclk_wait_sync();
+
+       /* wait for fine lock */
+       while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_DFLLLCKC)) == 0 ||
+              (samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_DFLLLCKF)) == 0)
+               ;
+#else
+       samd21_sysctrl.dfllmul = (((31/4) << SAMD21_SYSCTRL_DFLLMUL_CSTEP) |
+                                 ((255/4) << SAMD21_SYSCTRL_DFLLMUL_FSTEP) |
+                                 ((AO_DFLL48M / 1000) << SAMD21_SYSCTRL_DFLLMUL_MUL));
+
+       /* pull out coarse calibration value from rom */
+       uint32_t coarse = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL) &
+                          SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL_MASK);
+
+       samd21_sysctrl.dfllval = ((coarse << SAMD21_SYSCTRL_DFLLVAL_COARSE) |
+                                 (512 << SAMD21_SYSCTRL_DFLLVAL_FINE));
+
+       samd21_sysctrl.dfllctrl = 0;
+       samd21_dfll_wait_sync();
+
+       samd21_sysctrl.dfllctrl = ((1 << SAMD21_SYSCTRL_DFLLCTRL_MODE) |
+                                  (1 << SAMD21_SYSCTRL_DFLLCTRL_BPLCKC) |
+                                  (1 << SAMD21_SYSCTRL_DFLLCTRL_CCDIS) |
+                                  (1 << SAMD21_SYSCTRL_DFLLCTRL_USBCRM) |
+                                  (1 << SAMD21_SYSCTRL_DFLLCTRL_ENABLE));
+
+       samd21_dfll_wait_sync();
+       samd21_gclk_wait_sync();
+#endif
+
+       /*
+        * Switch generator to DFLL48M
+        */
+
+       /* divide by 1 */
+       samd21_gclk_gendiv(AO_GCLK_SYSCLK, 1);
+
+       /* select DFLL48M as source */
+       samd21_gclk_genctrl(SAMD21_GCLK_GENCTRL_SRC_DFLL48M, AO_GCLK_DFLL48M);
+#endif
+
+       /* Set up all of the clocks to be /1 */
+
+       samd21_pm.cpusel = ((0 << SAMD21_PM_CPUSEL_CPUDIV));
+       samd21_pm.apbasel = ((0 << SAMD21_PM_APBASEL_APBADIV));
+       samd21_pm.apbbsel = ((0 << SAMD21_PM_APBBSEL_APBBDIV));
+       samd21_pm.apbcsel = ((0 << SAMD21_PM_APBCSEL_APBCDIV));
+
+       /* Disable OSC8M */
+       samd21_sysctrl.osc8m &= ~(1UL << SAMD21_SYSCTRL_OSC8M_ENABLE);
+
+       /* Additional misc configuration stuff */
+
+       /* Disable automatic NVM write operations */
+       samd21_nvmctrl.ctrlb |= (1UL << SAMD21_NVMCTRL_CTRLB_MANW);
+}
diff --git a/src/samd21/ao_usb_samd21.c b/src/samd21/ao_usb_samd21.c
new file mode 100644 (file)
index 0000000..bed4f54
--- /dev/null
@@ -0,0 +1,875 @@
+/*
+ * 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_usb.h"
+#include "ao_product.h"
+
+#ifndef AO_POWER_MANAGEMENT
+#define AO_POWER_MANAGEMENT    0
+#endif
+
+#ifndef AO_USB_DEVICE_ID_SERIAL
+#define AO_USB_DEVICE_ID_SERIAL 0
+#endif
+
+#if USE_USB_FIFO
+static struct ao_fifo  ao_usb_rx_fifo;
+#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
+
+#define SAMD21_USB_ALIGN       __attribute__ ((aligned(4)))
+
+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;
+
+struct samd21_usb_desc samd21_usb_desc[8] SAMD21_USB_ALIGN;
+
+/* Pending EP0 IN data */
+static uint8_t         ao_usb_ep0_in_tmp[2];   /* small buffer */
+static const uint8_t   *ao_usb_ep0_in_data;    /* Remaining data */
+static uint8_t                 ao_usb_ep0_in_len;      /* Remaining amount */
+
+/* Pending EP0 OUT data */
+static uint8_t *ao_usb_ep0_out_data;
+static uint8_t         ao_usb_ep0_out_len;
+
+/* Endpoint 0 buffers */
+static uint8_t ao_usb_ep0_in_buf[AO_USB_CONTROL_SIZE] SAMD21_USB_ALIGN;
+static uint8_t ao_usb_ep0_out_buf[AO_USB_CONTROL_SIZE]  SAMD21_USB_ALIGN;
+
+#if AO_USB_HAS_INT
+/* Pointer to interrupt buffer in USB memory */
+static uint8_t ao_usb_int_buf[AO_USB_INT_SIZE]  SAMD21_USB_ALIGN;
+#endif
+
+/* Buffers in DRAM */
+#if AO_USB_HAS_IN
+static uint8_t ao_usb_in_tx_which;
+static uint8_t ao_usb_tx_count;
+static uint8_t ao_usb_in_buf[2][AO_USB_IN_SIZE]  SAMD21_USB_ALIGN;
+
+#endif
+#if AO_USB_HAS_OUT
+static uint8_t ao_usb_out_rx_which;
+#if !USE_USB_FIFO
+static uint8_t ao_usb_rx_count, ao_usb_rx_pos;
+#endif
+static uint8_t ao_usb_out_buf[2][AO_USB_OUT_SIZE]  SAMD21_USB_ALIGN;
+
+#endif
+
+/* 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_SETUP   1
+#define AO_USB_EP0_GOT_RX_DATA 2
+#define AO_USB_EP0_GOT_TX_ACK  4
+
+static uint8_t ao_usb_ep0_receive;
+static uint8_t ao_usb_address;
+static uint8_t ao_usb_address_pending;
+
+/*
+ * Set current device address and mark the
+ * interface as active
+ */
+static void
+ao_usb_set_address(uint8_t address)
+{
+       samd21_usb.dadd = (1 << SAMD21_USB_DADD_ADDEN) | (address << SAMD21_USB_DADD_DADD);
+       ao_usb_address_pending = 0;
+}
+
+/*
+ * Initialize an entpoint
+ */
+
+static void
+ao_usb_init_bank(struct samd21_usb_desc_bank *bank,
+                uint8_t *buf,
+                uint16_t size)
+{
+       bank->addr = (uint32_t) buf;
+
+       uint32_t size_bits = 0;
+       switch (size) {
+       case 8: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_8; break;
+       case 16: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_16; break;
+       case 32: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_32; break;
+       case 64: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_64; break;
+       case 128: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_128; break;
+       case 256: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_256; break;
+       case 512: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_512; break;
+       case 1023: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_1023; break;
+       }
+       bank->pcksize = ((0 << SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT) |
+                        (0 << SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE) |
+                        (size_bits << SAMD21_USB_DESC_PCKSIZE_SIZE) |
+                        (0 << SAMD21_USB_DESC_PCKSIZE_AUTO_ZLP));
+}
+
+static void
+ao_usb_init_ep(uint8_t ep,
+              uint8_t type_out, void *out_buf, uint16_t out_size,
+              uint8_t type_in, void *in_buf, uint16_t in_size)
+{
+       /* set up descriptors */
+       ao_usb_init_bank(&samd21_usb_desc[ep].bank[0],
+                        out_buf, out_size);
+
+       ao_usb_init_bank(&samd21_usb_desc[ep].bank[1],
+                        in_buf, in_size);
+
+       samd21_usb.ep[ep].epcfg = (uint8_t) ((type_out << SAMD21_USB_EP_EPCFG_EP_TYPE_OUT) |
+                                            (type_in << SAMD21_USB_EP_EPCFG_EP_TYPE_IN));
+
+       /* Clear all status bits */
+       samd21_usb.ep[ep].epstatusclr = 0xff;
+
+       /* Select interrupts */
+       uint8_t epinten = 0;
+       if (out_buf)
+               epinten |= (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT0);
+       if (in_buf)
+               epinten |= (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT1);
+       if (ep == 0)
+               epinten |= (1 << SAMD21_USB_EP_EPINTFLAG_RXSTP);
+       samd21_usb.ep[ep].epintenset = epinten;
+}
+
+static void
+ao_usb_set_ep0(void)
+{
+       ao_usb_init_ep(AO_USB_CONTROL_EP,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_CONTROL,
+                      ao_usb_ep0_out_buf, AO_USB_CONTROL_SIZE,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_IN_CONTROL,
+                      ao_usb_ep0_in_buf, AO_USB_CONTROL_SIZE);
+
+       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 = NULL;
+       ao_usb_ep0_out_len = 0;
+}
+
+static void
+ao_usb_set_configuration(void)
+{
+#if AO_USB_HAS_INT
+       ao_usb_init_ep(AO_USB_INT_EP,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DISABLED,
+                      NULL, 0,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_IN_INTERRUPT,
+                      ao_usb_int_buf, AO_USB_INT_SIZE);
+#endif
+
+#if AO_USB_HAS_OUT
+       ao_usb_init_ep(AO_USB_OUT_EP,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_BULK,
+                      &ao_usb_out_buf[0][0], AO_USB_OUT_SIZE,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_IN_DUAL_BANK,
+                      &ao_usb_out_buf[1][0], AO_USB_OUT_SIZE);
+
+       /* At first receive, we'll flip this back to 0 */
+       ao_usb_out_rx_which = 1;
+#endif
+
+#if AO_USB_HAS_IN
+       /* Set up the IN end point */
+       ao_usb_init_ep(AO_USB_IN_EP,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DUAL_BANK,
+                      &ao_usb_in_buf[0][0], AO_USB_IN_SIZE,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_IN_BULK,
+                      &ao_usb_in_buf[1][0], AO_USB_IN_SIZE);
+
+       /* First transmit data goes to buffer 0 */
+       ao_usb_in_tx_which = 0;
+#endif
+
+       ao_usb_in_flushed = 1;
+       ao_usb_in_pending = 0;
+       ao_wakeup(&ao_usb_in_pending);
+
+       ao_usb_out_avail = 0;
+       ao_usb_configuration = 0;
+
+       ao_wakeup(AO_USB_OUT_SLEEP_ADDR);
+}
+
+/* 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 ((samd21_usb.ep[0].epstatus & (1 << (SAMD21_USB_EP_EPSTATUS_BK1RDY))) != 0)
+               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;
+
+       memcpy(ao_usb_ep0_in_buf, ao_usb_ep0_in_data, this_len);
+       ao_usb_ep0_in_data += this_len;
+
+       /* Mark the endpoint as TX valid to send the packet */
+       samd21_usb_desc_set_byte_count(AO_USB_CONTROL_EP, 1, this_len);
+       samd21_usb_ep_set_ready(AO_USB_CONTROL_EP, 1);
+}
+
+/* Read data from the ep0 OUT fifo */
+static void
+ao_usb_ep0_fill(void)
+{
+       uint16_t        len = samd21_usb_desc_get_byte_count(AO_USB_CONTROL_EP, 0);
+
+       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 */
+       memcpy(ao_usb_ep0_out_data, ao_usb_ep0_out_buf, len);
+       ao_usb_ep0_out_data += len;
+
+       /* ACK the packet */
+       samd21_usb_ep_clr_ready(AO_USB_CONTROL_EP, 0);
+}
+
+static void
+ao_usb_ep0_in_reset(void)
+{
+       ao_usb_ep0_in_data = ao_usb_ep0_in_tmp;
+       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_tmp))
+               ao_usb_ep0_in_tmp[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};
+
+#if AO_USB_DEVICE_ID_SERIAL
+static uint8_t ao_usb_serial[2 + 64];
+
+/* Convert a 32-bit value to 8 hexidecimal UCS2 characters */
+static void
+hex_to_ucs2(uint32_t in, uint8_t *out)
+{
+       int     i;
+
+       for (i = 28; i >= 0; i -= 4) {
+               uint8_t bits = (in >> i) & 0xf;
+               *out++ = ((bits < 10) ? '0' : ('a' - 10)) + bits;
+               *out++ = 0;
+       }
+}
+
+/* Encode the device ID (128 bits) in hexidecimal to use as a device
+ * serial number
+ */
+static void
+ao_usb_serial_init(void)
+{
+       ao_usb_serial[0] = 66;  /* length */
+       ao_usb_serial[1] = AO_USB_DESC_STRING;
+       hex_to_ucs2(samd21_serial.word0, ao_usb_serial + 2 + 0);
+       hex_to_ucs2(samd21_serial.word1, ao_usb_serial + 2 + 16);
+       hex_to_ucs2(samd21_serial.word2, ao_usb_serial + 2 + 32);
+       hex_to_ucs2(samd21_serial.word3, ao_usb_serial + 2 + 48);
+}
+#endif
+
+/* 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 AO_USB_DEVICE_ID_SERIAL
+                       /* Slightly hacky - the serial number is string 3 */
+                       if (type == AO_USB_DESC_STRING && (value & 0xff) == 3) {
+                               descriptor = ao_usb_serial;
+                               len = sizeof (ao_usb_serial);
+                       }
+#endif
+                       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)
+               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:
+               switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
+               case AO_USB_RECIP_DEVICE:
+                       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_SET_ADDRESS:
+                               ao_usb_address = (uint8_t) ao_usb_setup.value;
+                               ao_usb_address_pending = 1;
+                               break;
+                       case AO_USB_REQ_GET_DESCRIPTOR:
+                               ao_usb_get_descriptor(ao_usb_setup.value, ao_usb_setup.length);
+                               break;
+                       case AO_USB_REQ_GET_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;
+                               ao_usb_set_configuration();
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_INTERFACE:
+                       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:
+                       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:
+               switch (ao_usb_setup.request) {
+               case AO_USB_SET_LINE_CODING:
+                       ao_usb_ep0_out_set((uint8_t *) &ao_usb_line_coding, 7);
+                       break;
+               case AO_USB_GET_LINE_CODING:
+                       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_SETUP) {
+               ao_usb_ep0_setup();
+       }
+       if (receive & AO_USB_EP0_GOT_RX_DATA) {
+               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) {
+
+#if HAS_FLIGHT && AO_USB_FORCE_IDLE
+               ao_flight_force_idle = 1;
+#endif
+               if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+                       ao_usb_ep0_flush();
+               /* 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_POWER_MANAGEMENT
+static void
+ao_usb_suspend(void)
+{
+       stm_usb.cntr |= (1 << STM_USB_CNTR_FSUSP);
+       ao_power_suspend();
+       stm_usb.cntr |= (1 << STM_USB_CNTR_LP_MODE);
+       ao_clock_suspend();
+}
+
+static void
+ao_usb_wakeup(void)
+{
+       ao_clock_resume();
+       stm_usb.cntr &= ~(1 << STM_USB_CNTR_FSUSP);
+       ao_power_resume();
+}
+#endif
+
+#if USE_USB_FIFO
+static void
+ao_usb_fifo_check(void)
+{
+       uint8_t next_which = 1 - ao_usb_out_rx_which;
+       if (ao_usb_out_avail & (1 << next_which)) {
+               uint8_t *buf = ao_usb_out_buf[next_which];
+               uint16_t len = samd21_usb_desc_get_byte_count(AO_USB_OUT_EP, next_which);
+
+               if (ao_fifo_has_space(&ao_usb_rx_fifo, len)) {
+                       uint16_t        i;
+
+                       for (i = 0; i < len; i++)
+                               ao_fifo_insert(&ao_usb_rx_fifo, buf[i]);
+                       samd21_usb_ep_clr_ready(AO_USB_OUT_EP, next_which);
+                       ao_usb_out_avail &= ~(1 << next_which);
+                       ao_usb_out_rx_which = next_which;
+                       ao_wakeup(AO_USB_OUT_SLEEP_ADDR);
+               }
+#if AO_USB_OUT_HOOK
+               ao_usb_out_hook(buf, len);
+#endif
+       }
+}
+#endif
+
+void
+samd21_usb_isr(void)
+{
+       uint16_t        intflag = samd21_usb.intflag;
+       uint16_t        epintsmry = samd21_usb.epintsmry;
+
+       samd21_usb.intflag = intflag;
+
+       if (epintsmry & (1 << 0)) {
+               uint8_t epintflag = samd21_usb.ep[0].epintflag;
+               samd21_usb.ep[0].epintflag = epintflag;
+
+               if (epintflag & (1 << SAMD21_USB_EP_EPINTFLAG_RXSTP))
+                       ao_usb_ep0_receive |= AO_USB_EP0_GOT_SETUP;
+               else if (epintflag & (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT0))
+                       ao_usb_ep0_receive |= AO_USB_EP0_GOT_RX_DATA;
+               if (epintflag & (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT1))
+                       ao_usb_ep0_receive |= AO_USB_EP0_GOT_TX_ACK;
+               ao_usb_ep0_handle(ao_usb_ep0_receive);
+       }
+#if AO_USB_HAS_OUT
+       if (epintsmry & (1 << AO_USB_OUT_EP)) {
+               uint8_t epintflag = samd21_usb.ep[AO_USB_OUT_EP].epintflag;
+               samd21_usb.ep[AO_USB_OUT_EP].epintflag = epintflag;
+               uint8_t avail = (epintflag >> SAMD21_USB_EP_EPINTFLAG_TRCPT0) & 3;
+               if (avail) {
+                       ao_usb_out_avail |= avail;
+                       ao_usb_running = 1;
+#if USE_USB_FIFO
+                       ao_usb_fifo_check();
+#else
+                       ao_wakeup(AO_USB_OUT_SLEEP_ADDR);
+#endif
+               }
+       }
+#endif
+#if AO_USB_HAS_IN
+       if (epintsmry & (1 << AO_USB_IN_EP)) {
+               uint8_t epintflag = samd21_usb.ep[AO_USB_IN_EP].epintflag;
+               samd21_usb.ep[AO_USB_IN_EP].epintflag = epintflag;
+               uint8_t done = (epintflag >> SAMD21_USB_EP_EPINTFLAG_TRCPT0) & 3;
+               if (done) {
+                       ao_usb_in_pending &= (uint8_t) ~done;
+                       ao_wakeup(&ao_usb_in_pending);
+               }
+       }
+#endif
+#if AO_USB_HAS_INT
+       if (epintsmry & (1 << AO_USB_INT_EP)) {
+       }
+#endif
+       if (intflag & (1 << SAMD21_USB_INTFLAG_EORST)) {
+               ao_usb_set_ep0();
+       }
+#if AO_POWER_MANAGEMENT
+       if (istr & (1 << STM_USB_ISTR_SUSP))
+               ao_usb_suspend();
+
+       if (istr & (1 << STM_USB_ISTR_WKUP))
+               ao_usb_wakeup();
+#endif
+}
+
+#if AO_USB_HAS_IN
+/* Queue the current IN buffer for transmission */
+static void
+_ao_usb_in_send(void)
+{
+       ao_usb_in_pending |= (uint8_t) (1 << ao_usb_in_tx_which);
+       if (ao_usb_tx_count != AO_USB_IN_SIZE)
+               ao_usb_in_flushed = 1;
+
+       samd21_usb_desc_set_byte_count(AO_USB_IN_EP, ao_usb_in_tx_which, ao_usb_tx_count);
+       samd21_usb_ep_set_ready(AO_USB_IN_EP, ao_usb_in_tx_which);
+
+       /* Toggle our usage */
+       ao_usb_in_tx_which = 1 - ao_usb_in_tx_which;
+       ao_usb_tx_count = 0;
+}
+
+/* Wait for a free IN buffer. Interrupts are blocked */
+static void
+_ao_usb_in_wait(void)
+{
+       /* Wait for an IN buffer to be ready */
+       while ((ao_usb_in_pending & (1 << ao_usb_in_tx_which)) != 0)
+               ao_sleep(&ao_usb_in_pending);
+}
+
+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();
+       if (!ao_usb_in_flushed) {
+               _ao_usb_in_wait();
+               if (!ao_usb_in_flushed)
+                       _ao_usb_in_send();
+       }
+       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_in_buf[ao_usb_in_tx_which][ao_usb_tx_count++] = c;
+
+       /* Send the packet when full */
+       if (ao_usb_tx_count == AO_USB_IN_SIZE)
+               _ao_usb_in_send();
+
+       ao_arch_release_interrupts();
+       if (c == '\n')
+               ao_usb_flush();
+}
+#endif
+
+#if AO_USB_HAS_OUT
+#if !USE_USB_FIFO
+static bool
+_ao_usb_out_recv(void)
+{
+       uint8_t next_which = 1 - ao_usb_out_rx_which;
+
+       if ((ao_usb_out_avail & (1 << next_which)) != 0) {
+               /* switch current buffer */
+               ao_usb_out_rx_which = next_which;
+               ao_usb_out_avail &= (uint8_t) ~(1 << ao_usb_out_rx_which);
+
+               ao_usb_rx_count = (uint8_t) samd21_usb_desc_get_byte_count(AO_USB_OUT_EP, ao_usb_out_rx_which);
+               ao_usb_rx_pos = 0;
+               return true;
+       }
+       return false;
+}
+#endif
+
+static int
+_ao_usb_pollchar(void)
+{
+       uint8_t c;
+
+       if (!ao_usb_running)
+               return AO_READ_AGAIN;
+
+#if USE_USB_FIFO
+       if (ao_fifo_empty(&ao_usb_rx_fifo))
+               return AO_READ_AGAIN;
+       c = ao_fifo_remove(&ao_usb_rx_fifo);
+       ao_usb_fifo_check();
+#else
+       for (;;) {
+               if (ao_usb_rx_pos != ao_usb_rx_count)
+                       break;
+
+               /* Check for packet */
+               if (!_ao_usb_out_recv())
+                       return AO_READ_AGAIN;
+       }
+
+       /* Pull a character out of the fifo */
+       c = ao_usb_out_buf[ao_usb_out_rx_which][ao_usb_rx_pos++];
+
+       if (ao_usb_rx_pos == ao_usb_rx_count)
+               samd21_usb_ep_clr_ready(AO_USB_OUT_EP, ao_usb_out_rx_which);
+#endif
+
+       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;
+}
+
+#endif
+
+void
+ao_usb_disable(void)
+{
+       ao_arch_block_interrupts();
+       samd21_nvic_clear_enable(SAMD21_NVIC_ISR_USB_POS);
+
+       /* Disable USB pull-up */
+       samd21_usb.ctrlb |= (1 << SAMD21_USB_CTRLB_DETACH);
+
+       /* Switch off the device */
+       samd21_usb.ctrla &= (uint8_t) ~(1 << SAMD21_USB_CTRLA_ENABLE);
+
+       /* Disable the interface */
+       samd21_pm.apbbmask &= ~(1UL << SAMD21_PM_APBBMASK_USB);
+       ao_arch_release_interrupts();
+}
+
+void
+ao_usb_enable(void)
+{
+       int     t;
+
+       ao_enable_port(&samd21_port_a);
+
+       /* Set up USB DM/DP pins */
+       samd21_port_pmux_set(&samd21_port_a, 24, SAMD21_PORT_PMUX_FUNC_G);
+       samd21_port_pmux_set(&samd21_port_a, 25, SAMD21_PORT_PMUX_FUNC_G);
+
+       /* Assign gclk 0 to USB reference */
+       samd21_gclk_clkctrl(AO_GCLK_USB, SAMD21_GCLK_CLKCTRL_ID_USB);
+
+       /* Enable USB clock */
+       samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_USB);
+
+       /* Reset USB Device */
+
+       samd21_usb.ctrla |= (1 << SAMD21_USB_CTRLA_SWRST);
+
+       while ((samd21_usb.ctrla & (1 << SAMD21_USB_CTRLA_SWRST)) != 0)
+               ;
+
+       while ((samd21_usb.syncbusy & (1 << SAMD21_USB_SYNCBUSY_SWRST)) != 0)
+               ;
+
+       memset(&samd21_usb_desc, 0, sizeof (samd21_usb_desc));
+
+       /* Detach */
+       samd21_usb.ctrlb |= (1 << SAMD21_USB_CTRLB_DETACH);
+
+       samd21_usb.descadd = (uint32_t) &samd21_usb_desc;
+
+       /* Load calibration values */
+       uint32_t transn = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_USB_TRANSN) &
+                          SAMD21_AUX1_CALIBRATION_USB_TRANSN_MASK);
+       if (transn == 0x1f)
+               transn = 5;
+       uint32_t transp = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_USB_TRANSP) &
+                          SAMD21_AUX1_CALIBRATION_USB_TRANSP_MASK);
+       if (transp == 0x1f)
+               transp = 29;
+       uint32_t trim   = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_USB_TRIM) &
+                          SAMD21_AUX1_CALIBRATION_USB_TRIM_MASK);
+       if (trim == 7)
+               trim = 3;
+
+       samd21_usb.padcal = (uint16_t) ((transn << SAMD21_USB_PADCAL_TRANSN) |
+                                       (transp << SAMD21_USB_PADCAL_TRANSP) |
+                                       (trim << SAMD21_USB_PADCAL_TRIM));
+
+       samd21_usb.qosctrl = ((3 << SAMD21_USB_QOSCTRL_CQOS) |
+                             (3 << SAMD21_USB_QOSCTRL_DQOS));
+
+       ao_arch_block_interrupts();
+
+       /* set full speed */
+       samd21_usb.ctrlb = (SAMD21_USB_CTRLB_SPDCONF_FS << SAMD21_USB_CTRLB_SPDCONF);
+
+       /* Set device mode */
+       samd21_usb.ctrla = ((0 << SAMD21_USB_CTRLA_MODE) |
+                           (1 << SAMD21_USB_CTRLA_RUNSTDBY) |
+                           (1 << SAMD21_USB_CTRLA_ENABLE));
+
+       while ((samd21_usb.syncbusy & (1 << SAMD21_USB_SYNCBUSY_ENABLE)) != 0)
+               ;
+
+       /* configure interrupts */
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_USB_POS);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_USB_POS, 2);
+
+       samd21_usb.intenset = ((1 << SAMD21_USB_INTFLAG_EORST));
+
+       ao_arch_release_interrupts();
+
+       for (t = 0; t < 50000; t++)
+               ao_arch_nop();
+
+       /* Attach */
+       samd21_usb.ctrlb &= (uint16_t) ~(1 << SAMD21_USB_CTRLB_DETACH);
+}
+
+void
+ao_usb_init(void)
+{
+       ao_usb_enable();
+
+#if AO_USB_DEVICE_ID_SERIAL
+       ao_usb_serial_init();
+#endif
+
+       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+#if USE_USB_STDIO
+       ao_add_stdio(_ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
+#endif
+}
diff --git a/src/samd21/registers.ld b/src/samd21/registers.ld
new file mode 100644 (file)
index 0000000..4ff885b
--- /dev/null
@@ -0,0 +1,53 @@
+samd21_scs     = 0xe000e000;
+samd21_systick = 0xe000e010;
+samd21_nvic    = 0xe000e100;
+samd21_scb     = 0xe000ed00;
+
+samd21_ahb_apb_a = 0x40000000;
+samd21_pac0    = 0x40000000;
+samd21_pm      = 0x40000400;
+samd21_sysctrl = 0x40000800;
+samd21_gclk    = 0x40000c00;
+samd21_wdt     = 0x40001000;
+samd21_rtc     = 0x40001400;
+samd21_rtc     = 0x40001400;
+samd21_eic     = 0x40001800;
+
+samd21_ahb_apb_b = 0x41000000;
+samd21_pac1    = 0x41000000;
+samd21_dsu     = 0x41002000;
+samd21_nvmctrl = 0x41004000;
+samd21_port_a  = 0x41004400;
+samd21_port_b  = 0x41004480;
+samd21_dmac    = 0x41004800;
+samd21_usb     = 0x41005000;
+samd21_mtb     = 0x41006000;
+
+samd21_ahb_apb_c = 0x42000000;
+samd21_pac2    = 0x42000000;
+samd21_evsys   = 0x42000400;
+samd21_sercom0 = 0x42000800;
+samd21_sercom1 = 0x42000c00;
+samd21_sercom2 = 0x42001000;
+samd21_sercom3 = 0x42001400;
+samd21_sercom4 = 0x42001800;
+samd21_sercom5 = 0x42001c00;
+samd21_tcc0    = 0x42002000;
+samd21_tcc1    = 0x42002400;
+samd21_tcc2    = 0x42002800;
+samd21_tc3     = 0x42002c00;
+samd21_tc4     = 0x42003000;
+samd21_tc5     = 0x42003400;
+samd21_tc6     = 0x42003800;
+samd21_tc7     = 0x42003c00;
+samd21_adc     = 0x42004000;
+samd21_ac      = 0x42004400;
+samd21_dac     = 0x42004800;
+samd21_ptc     = 0x42004c00;
+samd21_i2s     = 0x42005000;
+samd21_ac1     = 0x42005400;
+samd21_tcc3    = 0x42006000;
+
+samd21_aux0    = 0x00804000;
+samd21_aux1    = 0x00806000;
+samd21_serial  = 0x0080a000;
diff --git a/src/samd21/samd21.h b/src/samd21/samd21.h
new file mode 100644 (file)
index 0000000..b101c6b
--- /dev/null
@@ -0,0 +1,2102 @@
+/*
+ * Copyright © 2019 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 _SAMD21_H_
+#define _SAMD21_H_
+
+#include <stdint.h>
+
+typedef volatile uint64_t      vuint64_t;
+typedef volatile uint32_t      vuint32_t;
+typedef volatile void *                vvoid_t;
+typedef volatile uint16_t      vuint16_t;
+typedef volatile uint8_t       vuint8_t;
+
+struct samd21_pac {
+       vuint32_t       wpclr;
+       vuint32_t       wpset;
+};
+
+extern struct samd21_pac samd21_pac0;
+extern struct samd21_pac samd21_pac1;
+extern struct samd21_pac samd21_pac2;
+
+#define samd21_pac0 (*(struct samd21_pac *) 0x40000000)
+#define samd21_pac1 (*(struct samd21_pac *) 0x41000000)
+#define samd21_pac2 (*(struct samd21_pac *) 0x42000000)
+
+struct samd21_gclk {
+       vuint8_t        ctrl;
+       vuint8_t        status;
+       vuint16_t       clkctrl;
+       vuint32_t       genctrl;
+       vuint32_t       gendiv;
+};
+
+extern struct samd21_gclk samd21_gclk;
+
+#define samd21_gclk    (*(struct samd21_gclk *) 0x40000c00)
+
+#define SAMD21_GCLK_CTRL_SWRST         0
+
+#define SAMD21_GCLK_STATUS_SYNCBUSY    7
+
+#define SAMD21_GCLK_CLKCTRL_ID         0
+#define  SAMD21_GCLK_CLKCTRL_ID_DFLL48M_REF            0
+#define  SAMD21_GCLK_CLKCTRL_ID_DPLL                   1
+#define  SAMD21_GCLK_CLKCTRL_ID_DPLL_32K               2
+#define  SAMD21_GCLK_CLKCTRL_ID_WDT                    3
+#define  SAMD21_GCLK_CLKCTRL_ID_RTC                    4
+#define  SAMD21_GCLK_CLKCTRL_ID_EIC                    5
+#define  SAMD21_GCLK_CLKCTRL_ID_USB                    6
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_0                0x07
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_1                0x08
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_2                0x09
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_3                0x0a
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_4                0x0b
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_5                0x0c
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_6                0x0d
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_7                0x0e
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_8                0e0f
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_9                0x10
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_10       0x11
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_11       0x12
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOMx_SLOW           0x13
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOM0_CORE           0x14
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOM1_CORE           0x15
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOM2_CORE           0x16
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOM3_CORE           0x17
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOM4_CORE           0x18
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOM5_CORE           0x19
+#define  SAMD21_GCLK_CLKCTRL_ID_TCC0_TCC1              0x1a
+#define  SAMD21_GCLK_CLKCTRL_ID_TCC2_TC3               0x1b
+#define  SAMD21_GCLK_CLKCTRL_ID_TC4_TC5                        0x1c
+#define  SAMD21_GCLK_CLKCTRL_ID_TC6_TC7                        0x1d
+#define  SAMD21_GCLK_CLKCTRL_ID_ADC                    0x1e
+#define  SAMD21_GCLK_CLKCTRL_ID_AC_DIG                 0x1f
+#define  SAMD21_GCLK_CLKCTRL_ID_AC_ANA                 0x20
+#define  SAMD21_GCLK_CLKCTRL_ID_DAC                    0x21
+#define  SAMD21_GCLK_CLKCTRL_ID_PTC                    0x22
+#define  SAMD21_GCLK_CLKCTRL_ID_I2S_0                  0x23
+#define  SAMD21_GCLK_CLKCTRL_ID_I2S_1                  0x24
+#define  SAMD21_GCLK_CLKCTRL_ID_TCC3                   0x25
+
+#define SAMD21_GCLK_CLKCTRL_GEN                8
+#define SAMD21_GCLK_CLKCTRL_CLKEN      14
+#define SAMD21_GCLK_CLKCTRL_WRTLOCK    15
+
+#define SAMD21_GCLK_GENCTRL_ID         0
+#define SAMD21_GCLK_GENCTRL_SRC                8
+#define  SAMD21_GCLK_GENCTRL_SRC_XOSC          0
+#define  SAMD21_GCLK_GENCTRL_SRC_GCLKIN                1
+#define  SAMD21_GCLK_GENCTRL_SRC_GCLKGEN1      2
+#define  SAMD21_GCLK_GENCTRL_SRC_OSCULP32K     3
+#define  SAMD21_GCLK_GENCTRL_SRC_OSC32K                4
+#define  SAMD21_GCLK_GENCTRL_SRC_XOSC32K       5
+#define  SAMD21_GCLK_GENCTRL_SRC_OSC8M         6
+#define  SAMD21_GCLK_GENCTRL_SRC_DFLL48M       7
+#define  SAMD21_GCLK_GENCTRL_SRC_FDPLL96M      8
+
+#define SAMD21_GCLK_GENCTRL_GENEN      16
+#define SAMD21_GCLK_GENCTRL_IDC                17
+#define SAMD21_GCLK_GENCTRL_OOV                18
+#define SAMD21_GCLK_GENCTRL_OE         19
+#define SAMD21_GCLK_GENCTRL_DIVSEL     20
+#define SAMD21_GCLK_GENCTRL_RUNSTDBY   21
+
+#define SAMD21_GCLK_GENDIV_ID          0
+#define SAMD21_GCLK_GENDIV_DIV         8
+
+struct samd21_pm {
+       vuint8_t        ctrl;
+       vuint8_t        sleep;
+       vuint8_t        reserved_02;
+       vuint8_t        reserved_03;
+       vuint32_t       reserved_04;
+       vuint8_t        cpusel;
+       vuint8_t        apbasel;
+       vuint8_t        apbbsel;
+       vuint8_t        apbcsel;
+       vuint32_t       reserved_0c;
+
+       vuint32_t       reserved_10;
+       vuint32_t       ahbmask;
+       vuint32_t       apbamask;
+       vuint32_t       apbbmask;
+
+       vuint32_t       apbcmask;
+       vuint32_t       reserved_24;
+       vuint32_t       reserved_28;
+       vuint32_t       reserved_2c;
+
+       vuint32_t       reserved_30;
+       vuint8_t        intenclr;
+       vuint8_t        intelset;
+       vuint8_t        intflag;
+       vuint8_t        reserved_37;
+       vuint8_t        rcause;
+};
+
+extern struct samd21_pm samd21_pm;
+
+#define samd21_pm      (*(struct samd21_pm *) 0x40000400)
+
+#define SAMD21_PM_CPUSEL_CPUDIV                0
+#define SAMD21_PM_APBASEL_APBADIV      0
+#define SAMD21_PM_APBBSEL_APBBDIV      0
+#define SAMD21_PM_APBCSEL_APBCDIV      0
+
+#define SAMD21_PM_APBAMASK_PAC0                0
+#define SAMD21_PM_APBAMASK_PM          1
+#define SAMD21_PM_APBAMASK_SYSCTRL     2
+#define SAMD21_PM_APBAMASK_GCLK                3
+#define SAMD21_PM_APBAMASK_WDT         4
+#define SAMD21_PM_APBAMASK_RTC         5
+#define SAMD21_PM_APBAMASK_EIC         6
+
+#define SAMD21_PM_AHBMASK_HPB0         0
+#define SAMD21_PM_AHBMASK_HPB1         1
+#define SAMD21_PM_AHBMASK_HPB2         2
+#define SAMD21_PM_AHBMASK_DSU          3
+#define SAMD21_PM_AHBMASK_NVMCTRL      4
+#define SAMD21_PM_AHBMASK_DMAC         5
+#define SAMD21_PM_AHBMASK_USB          6
+
+#define SAMD21_PM_APBBMASK_PAC1                0
+#define SAMD21_PM_APBBMASK_DSU         1
+#define SAMD21_PM_APBBMASK_NVMCTRL     2
+#define SAMD21_PM_APBBMASK_PORT                3
+#define SAMD21_PM_APBBMASK_DMAC                4
+#define SAMD21_PM_APBBMASK_USB         5
+
+#define SAMD21_PM_APBCMASK_PAC2                0
+#define SAMD21_PM_APBCMASK_EVSYS       1
+#define SAMD21_PM_APBCMASK_SERCOM0     2
+#define SAMD21_PM_APBCMASK_SERCOM1     3
+#define SAMD21_PM_APBCMASK_SERCOM2     4
+#define SAMD21_PM_APBCMASK_SERCOM3     5
+#define SAMD21_PM_APBCMASK_SERCOM4     6
+#define SAMD21_PM_APBCMASK_SERCOM5     7
+#define SAMD21_PM_APBCMASK_TCC0                8
+#define SAMD21_PM_APBCMASK_TCC1                9
+#define SAMD21_PM_APBCMASK_TCC2                10
+#define SAMD21_PM_APBCMASK_TC3         11
+#define SAMD21_PM_APBCMASK_TC4         12
+#define SAMD21_PM_APBCMASK_TC5         13
+#define SAMD21_PM_APBCMASK_TC6         14
+#define SAMD21_PM_APBCMASK_TC7         15
+#define SAMD21_PM_APBCMASK_ADC         16
+#define SAMD21_PM_APBCMASK_AC          17
+#define SAMD21_PM_APBCMASK_DAC         18
+#define SAMD21_PM_APBCMASK_PTC         19
+#define SAMD21_PM_APBCMASK_I2S         20
+#define SAMD21_PM_APBCMASK_AC1         21
+#define SAMD21_PM_APBCMASK_TCC3                24
+
+struct samd21_sysctrl {
+       vuint32_t       intenclr;
+       vuint32_t       intenset;
+       vuint32_t       intflag;
+       vuint32_t       pclksr;
+
+       vuint32_t       xosc;
+       vuint32_t       xosc32k;
+       vuint32_t       osc32k;
+       vuint32_t       osculp32k;
+
+       vuint32_t       osc8m;
+       vuint32_t       dfllctrl;
+       vuint32_t       dfllval;
+       vuint32_t       dfllmul;
+
+       vuint32_t       dfllsync;
+       vuint32_t       bod33;
+       vuint32_t       reserved_38;
+       vuint32_t       vreg;
+
+       vuint32_t       vref;
+       vuint32_t       dpllctrla;
+       vuint32_t       dpllratio;
+       vuint32_t       dpllctrlb;
+
+       vuint32_t       dpllstatus;
+};
+
+extern struct samd21_sysctrl samd21_sysctrl;
+
+#define samd21_sysctrl (*(struct samd21_sysctrl *) 0x40000800)
+
+#define SAMD21_SYSCTRL_PCLKSR_XOSCRDY          0
+#define SAMD21_SYSCTRL_PCLKSR_XOSC32KRDY       1
+#define SAMD21_SYSCTRL_PCLKSR_OSC32KRDY                2
+#define SAMD21_SYSCTRL_PCLKSR_OSC8MRDY         3
+#define SAMD21_SYSCTRL_PCLKSR_DFLLRDY          4
+#define SAMD21_SYSCTRL_PCLKSR_DFLLOOB          5
+#define SAMD21_SYSCTRL_PCLKSR_DFLLLCKF         6
+#define SAMD21_SYSCTRL_PCLKSR_DFLLLCKC         7
+#define SAMD21_SYSCTRL_PCLKSR_DFLLRCS          8
+#define SAMD21_SYSCTRL_PCLKSR_BOD33RDY         9
+#define SAMD21_SYSCTRL_PCLKSR_BOD33DET         10
+#define SAMD21_SYSCTRL_PCLKSR_B33SRDY          11
+#define SAMD21_SYSCTRL_PCLKSR_DBPLLLCKR                15
+#define SAMD21_SYSCTRL_PCLKSR_DPLLLCKF         16
+#define SAMD21_SYSCTRL_PCLKSR_DPLLTO           17
+
+#define SAMD21_SYSCTRL_XOSC_ENABLE             1
+#define SAMD21_SYSCTRL_XOSC_XTALEN             2
+#define SAMD21_SYSCTRL_XOSC_RUNSTDBY           6
+#define SAMD21_SYSCTRL_XOSC_ONDEMAND           7
+#define SAMD21_SYSCTRL_XOSC_GAIN               8
+#define  SAMD21_SYSCTRL_XOSC_GAIN_2MHz                 0
+#define  SAMD21_SYSCTRL_XOSC_GAIN_4MHz                 1
+#define  SAMD21_SYSCTRL_XOSC_GAIN_8MHz                 2
+#define  SAMD21_SYSCTRL_XOSC_GAIN_16MHz                        3
+#define  SAMD21_SYSCTRL_XOSC_GAIN_30MHz                        4
+#define SAMD21_SYSCTRL_XOSC_AMPGC              11
+#define SAMD21_SYSCTRL_XOSC_STARTUP            12
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_1                 0
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_2                         1
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_4                 2
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_8                 3
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_16                        4
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_32                        5
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_64                        6
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_128               7
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_256               8
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_512               9
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_1024              10
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_2048              11
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_4096              12
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_8192              13
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_16384             14
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_32768             15
+
+#define SAMD21_SYSCTRL_XOSC32K_ENABLE  1
+#define SAMD21_SYSCTRL_XOSC32K_XTALEN  2
+#define SAMD21_SYSCTRL_XOSC32K_EN32K   3
+#define SAMD21_SYSCTRL_XOSC32K_AAMPEN  5
+#define SAMD21_SYSCTRL_XOSC32K_RUNSTDBY        6
+#define SAMD21_SYSCTRL_XOSC32K_ONDEMAND        7
+#define SAMD21_SYSCTRL_XOSC32K_STARTUP 8
+#define SAMD21_SYSCTRL_XOSC32K_WRTLOCK 12
+
+#define SAMD21_SYSCTRL_OSC8M_ENABLE    1
+#define SAMD21_SYSCTRL_OSC8M_RUNSTDBY  6
+#define SAMD21_SYSCTRL_OSC8M_ONDEMAND  7
+#define SAMD21_SYSCTRL_OSC8M_PRESC     8
+#define  SAMD21_SYSCTRL_OSC8M_PRESC_1          0
+#define  SAMD21_SYSCTRL_OSC8M_PRESC_2          1
+#define  SAMD21_SYSCTRL_OSC8M_PRESC_4          2
+#define  SAMD21_SYSCTRL_OSC8M_PRESC_8          3
+#define  SAMD21_SYSCTRL_OSC8M_PRESC_MASK       3
+#define SAMD21_SYSCTRL_OSC8M_CALIB     16
+#define SAMD21_SYSCTRL_OSC8M_FRANGE    30
+#define  SAMD21_SYSCTRL_OSC8M_FRANGE_4_6       0
+#define  SAMD21_SYSCTRL_OSC8M_FRANGE_6_8       1
+#define  SAMD21_SYSCTRL_OSC8M_FRANGE_8_11      2
+#define  SAMD21_SYSCTRL_OSC8M_FRANGE_11_15     3
+
+#define SAMD21_SYSCTRL_DFLLCTRL_ENABLE         1
+#define SAMD21_SYSCTRL_DFLLCTRL_MODE           2
+#define SAMD21_SYSCTRL_DFLLCTRL_STABLE         3
+#define SAMD21_SYSCTRL_DFLLCTRL_LLAW           4
+#define SAMD21_SYSCTRL_DFLLCTRL_USBCRM         5
+#define SAMD21_SYSCTRL_DFLLCTRL_RUNSTDBY       6
+#define SAMD21_SYSCTRL_DFLLCTRL_ONDEMAND       7
+#define SAMD21_SYSCTRL_DFLLCTRL_CCDIS          8
+#define SAMD21_SYSCTRL_DFLLCTRL_QLDIS          9
+#define SAMD21_SYSCTRL_DFLLCTRL_BPLCKC         10
+#define SAMD21_SYSCTRL_DFLLCTRL_WAITLOCK       11
+
+#define SAMD21_SYSCTRL_DFLLVAL_FINE            0
+#define SAMD21_SYSCTRL_DFLLVAL_COARSE          10
+#define SAMD21_SYSCTRL_DFLLVAL_DIFF            16
+
+#define SAMD21_SYSCTRL_DFLLMUL_MUL             0
+#define SAMD21_SYSCTRL_DFLLMUL_FSTEP           16
+#define SAMD21_SYSCTRL_DFLLMUL_CSTEP           26
+
+#define SAMD21_SYSCTRL_DFLLSYNC_READREQ                7
+
+#define SAMD21_SYSCTRL_DPLLCTRLA_ENABLE                1
+#define SAMD21_SYSCTRL_DPLLCTRLA_RUNSTDBY      6
+#define SAMD21_SYSCTRL_DPLLCTRLA_ONDEMAND      7
+
+#define SAMD21_SYSCTRL_DPLLRATIO_LDR           0
+#define SAMD21_SYSCTRL_DPLLRATIO_LDRFRAC       0
+
+#define SAMD21_SYSCTRL_DPLLCTRLB_FILTER                0
+#define  SAMD21_SYSCTRL_DPLLCTRLB_FILTER_DEFAULT       0
+#define  SAMD21_SYSCTRL_DPLLCTRLB_FILTER_LBFILT                1
+#define  SAMD21_SYSCTRL_DPLLCTRLB_FILTER_HBFILT                2
+#define  SAMD21_SYSCTRL_DPLLCTRLB_FILTER_HDFILT                3
+#define SAMD21_SYSCTRL_DPLLCTRLB_LPEN          2
+#define SAMD21_SYSCTRL_DPLLCTRLB_WUF           3
+#define SAMD21_SYSCTRL_DPLLCTRLB_REFCLK                4
+#define  SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_XOSC32                0
+#define  SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_XOSC          1
+#define  SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_GCLK_DPLL     2
+#define SAMD21_SYSCTRL_DPLLCTRLB_LTIME         8
+#define  SAMD21_SYSCTRL_DPLLCTRLB_LTIME_DEFAULT                0
+#define  SAMD21_SYSCTRL_DPLLCTRLB_LTIME_8MS            4
+#define  SAMD21_SYSCTRL_DPLLCTRLB_LTIME_9MS            5
+#define  SAMD21_SYSCTRL_DPLLCTRLB_LTIME_10MS           6
+#define  SAMD21_SYSCTRL_DPLLCTRLB_LTIME_11MS           7
+#define SAMD21_SYSCTRL_DPLLCTRLB_LBYPASS       12
+#define SAMD21_SYSCTRL_DPLLCTRLB_DIV           16
+
+#define SAMD21_SYSCTRL_DPLLSTATUS_LOCK         0
+#define SAMD21_SYSCTRL_DPLLSTATUS_CLKRDY       1
+#define SAMD21_SYSCTRL_DPLLSTATUS_ENABLE       2
+#define SAMD21_SYSCTRL_DPLLSTATUS_DIV          3
+
+struct samd21_dmac {
+       vuint16_t       ctrl;
+       vuint16_t       crcctrl;
+       vuint32_t       crcdatain;
+       vuint32_t       crcchksum;
+       vuint8_t        crcstatus;
+       vuint8_t        dbgctrl;
+       vuint8_t        qosctrl;
+       uint8_t         reserved_0f;
+
+       vuint32_t       swtrigctrl;
+       vuint32_t       prictrl0;
+       uint32_t        reserved_18;
+       uint32_t        reserved_1c;
+
+       vuint16_t       intpend;
+       uint16_t        reserved_22;
+       vuint32_t       intstatus;
+       vuint32_t       busych;
+       vuint32_t       pendch;
+
+       vuint32_t       active;
+       vuint32_t       baseaddr;
+       vuint32_t       wrbaddr;
+       uint16_t        reserved_3c;
+       uint8_t         reserved_3e;
+       vuint8_t        chid;
+
+       vuint8_t        chctrla;
+       uint8_t         reserved_41;
+       uint16_t        reserved_42;
+       vuint32_t       chctrlb;
+       uint32_t        reserved_48;
+       vuint8_t        chintenclr;
+       vuint8_t        chintenset;
+       vuint8_t        chintflag;
+       vuint8_t        chstatus;
+};
+
+extern struct samd21_dmac samd21_dmac;
+
+#define samd21_dmac (*(struct samd21_dmac *) 0x41004800)
+
+struct samd21_dmac_desc {
+       vuint16_t       btctrl;
+       vuint16_t       btcnt;
+       vuint32_t       srcaddr;
+       vuint32_t       dstaddr;
+       vuint32_t       descaddr;
+} __attribute__((aligned(8)));
+
+#define SAMD21_DMAC_NCHAN              12
+
+#define SAMD21_DMAC_CTRL_SWRST         0
+#define SAMD21_DMAC_CTRL_DMAENABLE     1
+#define SAMD21_DMAC_CTRL_CRCENABLE     2
+#define SAMD21_DMAC_CTRL_LVLEN(x)      (8 + (x))
+
+#define SAMD21_DMAC_QOSCTRL_WRBQOS     0
+#define SAMD21_DMAC_QOSCTRL_FQOS       2
+#define SAMD21_DMAC_QOSCTRL_DQOS       4
+
+#define SAMD21_DMAC_QOSCTRL_DISABLE    0
+#define SAMD21_DMAC_QOSCTRL_LOW                1
+#define SAMD21_DMAC_QOSCTRL_MEDIUM     2
+#define SAMD21_DMAC_QOSCTRL_HIGH       3
+
+#define SAMD21_DMAC_SWTRIGCTRL_SWTRIG(n)       (0 + (n))
+
+#define SAMD21_DMAC_PRICTRL0_LVLPRI0   0
+#define SAMD21_DMAC_PRICTRL0_RRLVLEN0  7
+#define SAMD21_DMAC_PRICTRL0_LVLPRI1   8
+#define SAMD21_DMAC_PRICTRL0_RRLVLEN1  15
+#define SAMD21_DMAC_PRICTRL0_LVLPRI2   16
+#define SAMD21_DMAC_PRICTRL0_RRLVLEN2  23
+#define SAMD21_DMAC_PRICTRL0_LVLPRI3   24
+#define SAMD21_DMAC_PRICTRL0_RRLVLEN3  31
+
+#define SAMD21_DMAC_INTPEND_ID         0
+#define  SAMD21_DMAC_INTPEND_ID_MASK           0xf
+#define SAMD21_DMAC_INTPEND_TERR       8
+#define SAMD21_DMAC_INTPEND_TCMPL      9
+#define SAMD21_DMAC_INTPEND_SUSP       10
+#define SAMD21_DMAC_INTPEND_FERR       13
+#define SAMD21_DMAC_INTPEND_BUSY       14
+#define SAMD21_DMAC_INTPEND_PEND       15
+
+#define SAMD21_DMAC_INTSTATUS_CHINT(n) (0 + (n))
+
+#define SAMD21_DMAC_BUSYCH_BUSYCH(n)   (0 + (n))
+
+#define SAMD21_DMAC_PENDCH_PENDCH(n)   (0 + (n))
+
+#define SAMD21_DMAC_ACTIVE_LVLEX(x)    (0 + (x))
+#define SAMD21_DMAC_ACTIVE_ID          8
+#define SAMD21_DMAC_ACTIVE_ABUSY       15
+#define SAMD21_DMAC_ACTIVE_BTCNT       16
+
+#define SAMD21_DMAC_CHCTRLA_SWRST      0
+#define SAMD21_DMAC_CHCTRLA_ENABLE     1
+
+#define SAMD21_DMAC_CHCTRLB_EVACT      0
+#define  SAMD21_DMAC_CHCTRLB_EVACT_NOACT       0
+#define  SAMD21_DMAC_CHCTRLB_EVACT_TRIG                1
+#define  SAMD21_DMAC_CHCTRLB_EVACT_CTRIG       2
+#define  SAMD21_DMAC_CHCTRLB_EVACT_CBLOCK      3
+#define  SAMD21_DMAC_CHCTRLB_EVACT_SUSPEND     4
+#define  SAMD21_DMAC_CHCTRLB_EVACT_RESUME      5
+#define  SAMD21_DMAC_CHCTRLB_EVACT_SSKIP       6
+
+#define SAMD21_DMAC_CHCTRLB_EVIE       3
+#define SAMD21_DMAC_CHCTRLB_EVOE       4
+#define SAMD21_DMAC_CHCTRLB_LVL                5
+#define  SAMD21_DMAC_CHCTRLB_LVL_LVL0          0UL
+#define  SAMD21_DMAC_CHCTRLB_LVL_LVL1          1UL
+#define  SAMD21_DMAC_CHCTRLB_LVL_LVL2          2UL
+#define  SAMD21_DMAC_CHCTRLB_LVL_LVL3          3UL
+#define SAMD21_DMAC_CHCTRLB_TRIGSRC    8
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_DISABLE   0x00UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_RX(n)      (0x01UL + (n) * 2UL)
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_TX(n)      (0x02UL + (n) * 2UL)
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_OVF  0x0dUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC0  0x0eUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC1  0x0fUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC2  0x10UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC3  0x11UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_OVF  0x12UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_MC0  0x13UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_MC1  0x14UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_OVF  0x15UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_MC0  0x16UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_MC1  0x17UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_OVF   0x18UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_MC0   0x19UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_MC1   0x1aUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_OVF   0x1bUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_MC0   0x1cUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_MC1   0x1dUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_OVF   0x1eUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_MC0   0x1fUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_MC1   0x20UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_OVF   0x21UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_MC0   0x22UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_MC1   0x23UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_OVF   0x24UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_MC0   0x25UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_MC1   0x26UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_ADC_RESRDY        0x27UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_DAC_EMPTY 0x28UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_RX_0  0x29UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_RX_1  0x2aUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_TX_0  0x2bUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_TX_1  0x2cUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_OVF  0x2dUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC0  0x2eUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC1  0x2fUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC2  0x30UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC3  0x31UL
+
+#define SAMD21_DMAC_CHCTRLB_TRIGACT    22
+#define  SAMD21_DMAC_CHCTRLB_TRIGACT_BLOCK             0UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGACT_BEAT              2UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGACT_TRANSACTION       3UL
+
+#define SAMD21_DMAC_CHCTRLB_CMD                24
+#define  SAMD21_DMAC_CHCTRLB_CMD_NOACT         0UL
+#define  SAMD21_DMAC_CHCTRLB_CMD_SUSPEND       1UL
+#define  SAMD21_DMAC_CHCTRLB_CMD_RESUME                2UL
+
+#define SAMD21_DMAC_CHINTFLAG_TERR     0
+#define SAMD21_DMAC_CHINTFLAG_TCMPL    1
+#define SAMD21_DMAC_CHINTFLAG_SUSP     2
+
+#define SAMD21_DMAC_CHSTATUS_PEND      0
+#define SAMD21_DMAC_CHSTATUS_BUSY      1
+#define SAMD21_DMAC_CHSTATUS_FERR      2
+
+#define SAMD21_DMAC_DESC_BTCTRL_VALID          0
+#define SAMD21_DMAC_DESC_BTCTRL_EVOSEL         1
+#define  SAMD21_DMAC_DESC_BTCTRL_EVOSEL_DISABLE                0UL
+#define  SAMD21_DMAC_DESC_BTCTRL_EVOSEL_BLOCK          1UL
+#define  SAMD21_DMAC_DESC_BTCTRL_EVOSEL_BEAT           3UL
+#define SAMD21_DMAC_DESC_BTCTRL_BLOCKACT       3
+#define  SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_NOACT                0UL
+#define  SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_INT          1UL
+#define  SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_SUSPEND      2UL
+#define  SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_BOTH         3UL
+#define SAMD21_DMAC_DESC_BTCTRL_BEATSIZE       8
+#define  SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_BYTE         0UL
+#define  SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_HWORD                1UL
+#define  SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_WORD         2UL
+#define SAMD21_DMAC_DESC_BTCTRL_SRCINC         10
+#define SAMD21_DMAC_DESC_BTCTRL_DSTINC         11
+#define SAMD21_DMAC_DESC_BTCTRL_STEPSEL                12
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSEL_DST           0UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSEL_SRC           1UL
+#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE       13
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X1           0UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X2           1UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X4           2UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X8           3UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X16          4UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X32          5UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X64          6UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X128         7UL
+
+struct samd21_eic {
+       vuint8_t        ctrl;
+       vuint8_t        status;
+       vuint8_t        nmictrl;
+       vuint8_t        nmiflag;
+       vuint32_t       evctrl;
+       vuint32_t       intenclr;
+       vuint32_t       intenset;
+
+       vuint32_t       intflag;
+       vuint32_t       wakeup;
+       vuint32_t       config[2];
+};
+
+extern struct samd21_eic samd21_eic;
+
+#define samd21_eic     (*(struct samd21_eic *) 0x40001800)
+
+#define SAMD21_NUM_EIC                 16
+
+#define SAMD21_EIC_CTRL_ENABLE         1
+#define SAMD21_EIC_CTRL_SWRST          0
+
+#define SAMD21_EIC_STATUS_SYNCBUSY     7
+
+#define SAMD21_EIC_NMICTRL_NMIFILTEN   3
+#define SAMD21_EIC_NMICTRL_NMISENSE    0
+
+#define SAMD21_EIC_NMIFLAG_NMI         0
+
+#define SAMD21_EIC_EVCTRL_EXTINTEO(n)  (n)
+
+#define SAMD21_EIC_INTENCLR_EXTINT(n)  (n)
+
+#define SAMD21_EIC_INTENSET_EXTINT(n)  (n)
+
+#define SAMD21_EIC_INTFLAG_EXTINT(n)   (n)
+#define SAMD21_EIC_WAKEUP_WAKEUPEN(n)  (n)
+#define SAMD21_EIC_CONFIG_N(n)         ((n) >> 3)
+#define SAMD21_EIC_CONFIG_SENSE(n)     (((n) & 7) << 2)
+#define SAMD21_EIC_CONFIG_FILTEN(n)    (SAMD21_EIC_CONFIG_SENSE(n) + 3)
+#define  SAMD21_EIC_CONFIG_SENSE_NONE          0
+#define  SAMD21_EIC_CONFIG_SENSE_RISE          1
+#define  SAMD21_EIC_CONFIG_SENSE_FALL          2
+#define  SAMD21_EIC_CONFIG_SENSE_BOTH          3
+#define  SAMD21_EIC_CONFIG_SENSE_HIGH          4
+#define  SAMD21_EIC_CONFIG_SENSE_LOW           5
+#define  SAMD21_EIC_CONFIG_SENSE_MASK          7UL
+
+struct samd21_nvmctrl {
+       vuint32_t       ctrla;
+       vuint32_t       ctrlb;
+       vuint32_t       param;
+       vuint32_t       intenclr;
+
+       vuint32_t       intenset;
+       vuint32_t       intflag;
+       vuint32_t       status;
+       vuint32_t       addr;
+
+       vuint32_t       lock;
+};
+
+extern struct samd21_nvmctrl samd21_nvmctrl;
+
+#define samd21_nvmctrl (*(struct samd21_nvmctrl *) 0x41004000)
+
+#define SAMD21_NVMCTRL_CTRLA_CMD       0
+#define  SAMD21_NVMCTRL_CTRLA_CMD_ER           0x02
+#define  SAMD21_NVMCTRL_CTRLA_CMD_WP           0x04
+#define  SAMD21_NVMCTRL_CTRLA_CMD_EAR          0x05
+#define  SAMD21_NVMCTRL_CTRLA_CMD_WAP          0x06
+#define  SAMD21_NVMCTRL_CTRLA_CMD_RWWEEER      0x1a
+#define  SAMD21_NVMCTRL_CTRLA_CMD_RWEEEWP      0x1c
+#define  SAMD21_NVMCTRL_CTRLA_CMD_LR           0x40
+#define  SAMD21_NVMCTRL_CTRLA_CMD_UR           0x41
+#define  SAMD21_NVMCTRL_CTRLA_CMD_SPRM         0x42
+#define  SAMD21_NVMCTRL_CTRLA_CMD_CPRM         0x43
+#define  SAMD21_NVMCTRL_CTRLA_CMD_PBC          0x44
+#define  SAMD21_NVMCTRL_CTRLA_CMD_SSB          0x45
+#define  SAMD21_NVMCTRL_CTRLA_CMD_INVALL       0x46
+#define  SAMD21_NVMCTRL_CTRLA_CMD_LDR          0x47
+#define  SAMD21_NVMCTRL_CTRLA_CMD_UDR          0x48
+#define SAMD21_NVMCTRL_CTRLA_CMDEX     8
+#define  SAMD21_NVMCTRL_CTRLA_CMDEX_KEY                0xa5
+
+#define SAMD21_NVMCTRL_CTRLB_RWS       1
+#define SAMD21_NVMCTRL_CTRLB_MANW      7
+#define SAMD21_NVMCTRL_CTRLB_SLEEPRM   8
+#define SAMD21_NVMCTRL_CTRLB_READMODE  16
+#define SAMD21_NVMCTRL_CTRLB_CACHEDIS  18
+
+#define SAMD21_NVMCTRL_INTENCLR_READY  0
+#define SAMD21_NVMCTRL_INTENCLR_ERROR  1
+
+#define SAMD21_NVMCTRL_INTENSET_READY  0
+#define SAMD21_NVMCTRL_INTENSET_ERROR  1
+
+#define SAMD21_NVMCTRL_INTFLAG_READY   0
+#define SAMD21_NVMCTRL_INTFLAG_ERROR   1
+
+#define SAMD21_NVMCTRL_STATUS_PRM      0
+#define SAMD21_NVMCTRL_STATUS_LOAD     1
+#define SAMD21_NVMCTRL_STATUS_PROGE    2
+#define SAMD21_NVMCTRL_STATUS_LOCKE    3
+#define SAMD21_NVMCTRL_STATUS_NVME     4
+#define SAMD21_NVMCTRL_STATUS_SB       8
+
+#define SAMD21_NVMCTRL_PARAM_NVMP      0
+#define  SAMD21_NVMCTRL_PARAM_NVMP_MASK                0xffff
+#define SAMD21_NVMCTRL_PARAM_PSZ       16
+#define  SAMD21_NVMCTRL_PARAM_PSZ_MASK         0x7
+#define SAMD21_NVMCTRL_PARAM_RWWEEP    20
+#define  SAMD21_NVMCTRL_PARAM_RWWEEP_MASK      0xfff
+
+static inline uint32_t
+samd21_nvmctrl_page_shift(void)
+{
+       return(3 + ((samd21_nvmctrl.param >> SAMD21_NVMCTRL_PARAM_PSZ) &
+                   SAMD21_NVMCTRL_PARAM_PSZ_MASK));
+}
+
+static inline uint32_t
+samd21_nvmctrl_page_size(void)
+{
+       return 1 << samd21_nvmctrl_page_shift();
+}
+
+uint32_t
+samd21_flash_size(void);
+
+struct samd21_port {
+       vuint32_t       dir;
+       vuint32_t       dirclr;
+       vuint32_t       dirset;
+       vuint32_t       dirtgl;
+
+       vuint32_t       out;
+       vuint32_t       outclr;
+       vuint32_t       outset;
+       vuint32_t       outtgl;
+
+       vuint32_t       in;
+       vuint32_t       ctrl;
+       vuint32_t       wrconfig;
+       vuint32_t       reserved_2c;
+
+       vuint8_t        pmux[16];
+
+       vuint8_t        pincfg[32];
+};
+
+extern struct samd21_port samd21_port_a;
+extern struct samd21_port samd21_port_b;
+
+#define samd21_port_a (*(struct samd21_port *) 0x41004400)
+#define samd21_port_b (*(struct samd21_port *) 0x41004480)
+
+#define SAMD21_PORT_PINCFG_PMUXEN      0
+#define SAMD21_PORT_PINCFG_INEN                1
+#define SAMD21_PORT_PINCFG_PULLEN      2
+#define SAMD21_PORT_PINCFG_DRVSTR      6
+
+#define SAMD21_PORT_PMUX_FUNC_A                0
+#define SAMD21_PORT_PMUX_FUNC_B                1
+#define SAMD21_PORT_PMUX_FUNC_C                2
+#define SAMD21_PORT_PMUX_FUNC_D                3
+#define SAMD21_PORT_PMUX_FUNC_E                4
+#define SAMD21_PORT_PMUX_FUNC_F                5
+#define SAMD21_PORT_PMUX_FUNC_G                6
+#define SAMD21_PORT_PMUX_FUNC_H                7
+#define SAMD21_PORT_PMUX_FUNC_I                8
+
+#define SAMD21_PORT_DIR_OUT            1
+#define SAMD21_PORT_DIR_IN             0
+
+static inline void
+samd21_port_dir_set(struct samd21_port *port, uint8_t pin, uint8_t dir)
+{
+       if (dir)
+               port->dirset = (1 << pin);
+       else
+               port->dirclr = (1 << pin);
+}
+
+static inline void
+samd21_port_pincfg_set(struct samd21_port *port, uint8_t pin, uint8_t pincfg_mask, uint8_t pincfg)
+{
+       port->pincfg[pin] = (uint8_t) ((port->pincfg[pin] & ~pincfg_mask) | pincfg);
+}
+
+static inline uint8_t
+samd21_port_pincfg_get(struct samd21_port *port, uint8_t pin)
+{
+       return port->pincfg[pin];
+}
+
+static inline void
+samd21_port_pmux_set(struct samd21_port *port, uint8_t pin, uint8_t func)
+{
+       uint8_t byte = pin >> 1;
+       uint8_t bit = (pin & 1) << 2;
+       uint8_t mask = 0xf << bit;
+       uint8_t value = (uint8_t) ((port->pmux[byte] & ~mask) | (func << bit));
+       port->pmux[byte] = value;
+       samd21_port_pincfg_set(port, pin,
+                              (1 << SAMD21_PORT_PINCFG_PMUXEN),
+                              (1 << SAMD21_PORT_PINCFG_PMUXEN));
+}
+
+static inline uint8_t
+samd21_port_pmux_get(struct samd21_port *port, uint8_t pin)
+{
+       uint8_t byte = pin >> 1;
+       uint8_t bit = (pin & 1) << 2;
+       uint8_t mask = 0xf << bit;
+       uint8_t value = (uint8_t) ((port->pmux[byte] & mask) >> bit);
+       return value;
+}
+
+static inline void
+samd21_port_pmux_clr(struct samd21_port *port, uint8_t pin)
+{
+       samd21_port_pincfg_set(port, pin,
+                              (0 << SAMD21_PORT_PINCFG_PMUXEN),
+                              (1 << SAMD21_PORT_PINCFG_PMUXEN));
+}
+
+struct samd21_adc {
+       vuint8_t        ctrla;
+       vuint8_t        refctrl;
+       vuint8_t        avgctrl;
+       vuint8_t        sampctrl;
+       vuint16_t       ctrlb;
+       vuint16_t       reserved_06;
+       vuint8_t        winctrl;
+       vuint8_t        reserved_09;
+       vuint16_t       reserved_0a;
+       vuint8_t        swtrig;
+       vuint8_t        reserved_0d;
+       vuint16_t       reserved_0e;
+
+       vuint32_t       inputctrl;
+       vuint8_t        evctrl;
+       vuint8_t        reserved_15;
+       vuint8_t        intenclr;
+       vuint8_t        intenset;
+       vuint8_t        intflag;
+       vuint8_t        status;
+       vuint16_t       result;
+       vuint16_t       winlt;
+       vuint16_t       reserved_1e;
+
+       vuint16_t       winut;
+       vuint16_t       reserved_22;
+       vuint16_t       gaincorr;
+       vuint16_t       offsetcorr;
+       vuint16_t       calib;
+       vuint8_t        dbgctrl;
+       vuint8_t        reserved_2b;
+       vuint32_t       reserved_2c;
+};
+
+#define SAMD21_ADC_CTRLA_SWRST         0
+#define SAMD21_ADC_CTRLA_ENABLE                1
+#define SAMD21_ADC_CTRLA_RUNSTDBY      2
+
+#define SAMD21_ADC_REFCTRL_REFSEL      0
+#define  SAMD21_ADC_REFCTRL_REFSEL_INT1V       0
+#define  SAMD21_ADC_REFCTRL_REFSEL_INTVCC0     1
+#define  SAMD21_ADC_REFCTRL_REFSEL_INTVCC1     2
+#define  SAMD21_ADC_REFCTRL_REFSEL_VREFA       3
+#define  SAMD21_ADC_REFCTRL_REFSEL_VREFB       4
+#define SAMD21_ADC_REFCTRL_REFCOMP     7
+
+#define SAMD21_ADC_AVGCTRL_SAMPLENUM   0
+#define SAMD21_ADC_AVGCTRL_ADJRES      4
+
+#define SAMD21_ADC_SAMPCTRL_SAMPLEN    0
+
+#define SAMD21_ADC_CTRLB_DIFFMODE      0
+#define SAMD21_ADC_CTRLB_LEFTADJ       1
+#define SAMD21_ADC_CTRLB_FREERUN       2
+#define SAMD21_ADC_CTRLB_CORREN                3
+#define SAMD21_ADC_CTRLB_RESSEL                4
+#define  SAMD21_ADC_CTRLB_RESSEL_12BIT         0
+#define  SAMD21_ADC_CTRLB_RESSEL_16BIT         1
+#define  SAMD21_ADC_CTRLB_RESSEL_10BIT         2
+#define  SAMD21_ADC_CTRLB_RESSEL_8BIT          3
+#define SAMD21_ADC_CTRLB_PRESCALER     8
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV4       0
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV8       1
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV16      2
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV32      3
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV64      4
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV128     5
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV256     6
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV512     7
+
+#define SAMD21_ADC_SWTRIG_FLUSH                0
+#define SAMD21_ADC_SWTRIG_START                1
+
+#define SAMD21_ADC_INPUTCTRL_MUXPOS            0
+# define SAMD21_ADC_INPUTCTRL_MUXPOS_TEMP              0x18
+# define SAMD21_ADC_INPUTCTRL_MUXPOS_BANDGAP           0x19
+# define SAMD21_ADC_INPUTCTRL_MUXPOS_SCALEDCOREVCC     0x1a
+# define SAMD21_ADC_INPUTCTRL_MUXPOS_SCALEDIOVCC       0x1b
+# define SAMD21_ADC_INPUTCTRL_MUXPOS_DAC               0x1c
+#define SAMD21_ADC_INPUTCTRL_MUXNEG            8
+# define SAMD21_ADC_INPUTCTRL_MUXNEG_GND               0x18
+# define SAMD21_ADC_INPUTCTRL_MUXNEG_IOGND             0x19
+#define SAMD21_ADC_INPUTCTRL_INPUTSCAN         16
+#define SAMD21_ADC_INPUTCTRL_INPUTOFFSET       20
+#define SAMD21_ADC_INPUTCTRL_GAIN              24
+#define  SAMD21_ADC_INPUTCTRL_GAIN_1X                  0
+#define  SAMD21_ADC_INPUTCTRL_GAIN_DIV2                        0xf
+
+#define SAMD21_ADC_INTFLAG_RESRDY      0
+#define SAMD21_ADC_INTFLAG_OVERRUN     1
+#define SAMD21_ADC_INTFLAG_WINMON      2
+#define SAMD21_ADC_INTFLAG_SYNCRDY     3
+
+#define SAMD21_ADC_STATUS_SYNCBUSY     7
+
+#define SAMD21_ADC_CALIB_LINEARITY_CAL 0
+#define SAMD21_ADC_CALIB_BIAS_CAL      16
+
+extern struct samd21_adc samd21_adc;
+
+#define samd21_adc (*(struct samd21_adc *) 0x42004000)
+
+struct samd21_dac {
+       vuint8_t        ctrla;
+       vuint8_t        ctrlb;
+       vuint8_t        evctrl;
+       uint8_t         reserved_03;
+
+       vuint8_t        intenclr;
+       vuint8_t        intenset;
+       vuint8_t        intflag;
+       vuint8_t        status;
+
+       vuint16_t       data;
+       uint16_t        reserved_0a;
+
+       vuint16_t       databuf;
+};
+
+#define SAMD21_DAC_CTRLA_SWRST         0
+#define SAMD21_DAC_CTRLA_ENABLE                1
+#define SAMD21_DAC_CTRLA_RUNSTDBY      2
+
+#define SAMD21_DAC_CTRLB_EOEN          0
+#define SAMD21_DAC_CTRLB_IOEN          1
+#define SAMD21_DAC_CTRLB_LEFTADJ       2
+#define SAMD21_DAC_CTRLB_VPD           3
+#define SAMD21_DAC_CTRLB_BDWP          4
+#define SAMD21_DAC_CTRLB_REFSEL                6
+#define  SAMD21_DAC_CTRLB_REFSEL_INTREF                0
+#define  SAMD21_DAC_CTRLB_REFSEL_VDDANA                1
+#define  SAMD21_DAC_CTRLB_REFSEL_VREFA         2
+#define  SAMD21_DAC_CTRLB_REFSEL_MASK          3
+
+#define SAMD21_DAC_EVCTRL_STARTEI      0
+#define SAMD21_DAC_EVCTRL_EMPTYEO      1
+
+#define SAMD21_DAC_INTENCLR_UNDERRUN   0
+#define SAMD21_DAC_INTENCLR_EMPTY      1
+#define SAMD21_DAC_INTENCLR_SYNCRDY    2
+
+#define SAMD21_DAC_INTENSET_UNDERRUN   0
+#define SAMD21_DAC_INTENSET_EMPTY      1
+#define SAMD21_DAC_INTENSET_SYNCRDY    2
+
+#define SAMD21_DAC_INTFLAG_UNDERRUN    0
+#define SAMD21_DAC_INTFLAG_EMPTY       1
+#define SAMD21_DAC_INTFLAG_SYNCRDY     2
+
+#define SAMD21_DAC_STATUS_SYNCBUSY     7
+
+extern struct samd21_dac samd21_dac;
+#define samd21_dac (*(struct samd21_dac *) 0x42004800)
+
+/* TC */
+struct samd21_tc {
+       vuint16_t       ctrla;
+       vuint16_t       readreq;
+       vuint8_t        ctrlbclr;
+       vuint8_t        ctrlbset;
+       vuint8_t        ctrlc;
+       vuint8_t        reserved_07;
+       vuint8_t        dbgctrl;
+       vuint8_t        reserved_09;
+       vuint16_t       evctrl;
+       vuint8_t        intenclr;
+       vuint8_t        intenset;
+       vuint8_t        intflag;
+       vuint8_t        status;
+
+       union {
+               struct {
+                       vuint8_t        count;
+                       vuint8_t        reserved_11;
+                       vuint16_t       reserved_12;
+                       vuint8_t        per;
+                       vuint8_t        reserved_15;
+                       vuint16_t       reserved_16;
+                       vuint8_t        cc[2];
+               } mode_8;
+               struct {
+                       vuint16_t       count;
+                       vuint16_t       reserved_12;
+                       vuint32_t       reserved_14;
+                       vuint16_t       cc[2];
+               } mode_16;
+               struct {
+                       vuint32_t       count;
+                       vuint32_t       reserved_14;
+                       vuint32_t       cc[2];
+               } mode_32;
+       };
+};
+
+extern struct samd21_tc samd21_tc3;
+#define samd21_tc3 (*(struct samd21_tc *) 0x42002c00)
+
+extern struct samd21_tc samd21_tc4;
+#define samd21_tc4 (*(struct samd21_tc *) 0x42003000)
+
+extern struct samd21_tc samd21_tc5;
+#define samd21_tc5 (*(struct samd21_tc *) 0x42003400)
+
+#ifdef ATSAMD21J
+/* Present on all of the samd21j parts and the samd21g16l */
+extern struct samd21_tc samd21_tc6;
+#define samd21_tc6 (*(struct samd21_tc *) 0x42003800)
+
+extern struct samd21_tc samd21_tc7;
+#define samd21_tc7 (*(struct samd21_tc *) 0x42003c00)
+#endif
+
+#define SAMD21_TC_CTRLA_SWRST          0
+#define SAMD21_TC_CTRLA_ENABLE         1
+#define SAMD21_TC_CTRLA_MODE           2
+#define  SAMD21_TC_CTRLA_MODE_COUNT16          0
+#define  SAMD21_TC_CTRLA_MODE_COUNT8           1
+#define  SAMD21_TC_CTRLA_MODE_COUNT32          2
+#define SAMD21_TC_CTRLA_WAVEGEN                5
+#define  SAMD21_TC_CTRLA_WAVEGEN_NFRQ          0
+#define  SAMD21_TC_CTRLA_WAVEGEN_MFRQ          1
+#define  SAMD21_TC_CTRLA_WAVEGEN_NPWM          2
+#define  SAMD21_TC_CTRLA_WAVEGEN_MPWM          3
+#define SAMD21_TC_CTRLA_PRESCALER      8
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV1                0
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV2                1
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV4                2
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV8                3
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV16       4
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV64       5
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV256      6
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV1024     7
+#define SAMD21_TC_CTRLA_RUNSTDBY       11
+#define SAMD21_TC_CTRLA_PRESCSYNC      12
+#define  SAMD21_TC_CTRLA_PRESCSYNC_GCLK                0
+#define  SAMD21_TC_CTRLA_PRESCSYNC_PRSEC       1
+#define  SAMD21_TC_CTRLA_PRESCSYNC_RESYNC      2
+
+#define SAMD21_TC_READREQ_ADDR         0
+#define SAMD21_TC_READREQ_RCONT                14
+#define SAMD21_TC_READREQ_RREQ         15
+#define SAMD21_TC_CTRLB_DIR            0
+#define SAMD21_TC_CTRLB_ONESHOT                2
+#define SAMD21_TC_CTRLB_CMD            6
+#define SAMD21_TC_CTRLC_INVEN(x)       (0 + (x))
+#define SAMD21_TC_CTRLC_CPTEN(x)       (4 + (x))
+#define SAMD21_TC_DBGCTRL_DBGRUN       0
+#define SAMD21_TC_EVCTRL_EVACT         0
+#define SAMD21_TC_EVCTRL_TCINV         4
+#define SAMD21_TC_EVCTRL_TCEI          5
+#define SAMD21_TC_EVCTRL_OVFEO         8
+#define SAMD21_TC_EVCTRL_MCEO(x)       (12 + (x))
+
+#define SAMD21_TC_INTFLAG_MC(x)                (4 + (x))
+#define SAMD21_TC_INTFLAG_SYNCRDY      3
+#define SAMD21_TC_INTFLAG_ERR          1
+#define SAMD21_TC_INTFLAG_OVF          0
+
+#define SAMD21_TC_STATUS_STOP          3
+#define SAMD21_TC_STATUS_FOLLOWER      4
+#define SAMD21_TC_STATUS_SYNCBUSY      7
+
+/* TCC */
+
+struct samd21_tcc {
+       vuint32_t       ctrla;
+       vuint8_t        ctrlbclr;
+       vuint8_t        ctrlbset;
+       vuint16_t       reserved_06;
+       vuint32_t       syncbusy;
+       vuint32_t       fctrla;
+
+       vuint32_t       fctlrb;
+       vuint32_t       wexctrl;
+       vuint32_t       drvctrl;
+       vuint16_t       reserved_1c;
+       vuint8_t        dbgctrl;
+       vuint8_t        reserved_1f;
+
+       vuint32_t       evctrl;
+       vuint32_t       intenclr;
+       vuint32_t       intenset;
+       vuint32_t       intflag;
+
+       vuint32_t       status;
+       vuint32_t       count;
+       vuint16_t       patt;
+       vuint16_t       reserved_3a;
+       vuint32_t       wave;
+
+       vuint32_t       per;
+       vuint32_t       cc[4];
+       vuint32_t       reserved_54;
+       vuint32_t       reserved_58;
+       vuint32_t       reserved_5c;
+
+       vuint32_t       reserved_60;
+       vuint16_t       pattb;
+       vuint16_t       reserved_66;
+       vuint32_t       waveb;
+       vuint32_t       perb;
+
+       vuint32_t       ccb[4];
+};
+
+extern struct samd21_tcc samd21_tcc0;
+#define samd21_tcc0 (*(struct samd21_tcc *) 0x42002000)
+
+extern struct samd21_tcc samd21_tcc1;
+#define samd21_tcc1 (*(struct samd21_tcc *) 0x42002400)
+
+extern struct samd21_tcc samd21_tcc2;
+#define samd21_tcc2 (*(struct samd21_tcc *) 0x42002800)
+
+#ifdef SAMD21E17D
+/* only on the samd21e17d */
+extern struct samd21_tcc samd21_tcc3;
+#define samd21_tcc3 (*(struct samd21_tcc *) 0x42006000)
+#endif
+
+#define SAMD21_TCC_CTRLA_SWRST         0
+#define SAMD21_TCC_CTRLA_ENABLE                1
+#define SAMD21_TCC_CTRLA_RESOLUTION    5
+#define  SAMD21_TCC_CTRLA_RESOLUTION_NONE      0
+#define  SAMD21_TCC_CTRLA_RESOLUTION_DITH4     1
+#define  SAMD21_TCC_CTRLA_RESOLUTION_DITH5     2
+#define  SAMD21_TCC_CTRLA_RESOLUTION_DITH6     3
+#define SAMD21_TCC_CTRLA_PRESCALER     8
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV1       0
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV2       1
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV4       2
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV8       3
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV16      4
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV64      5
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV256     6
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV1024    7
+#define SAMD21_TCC_CTRLA_RUNSTDBY      11
+#define SAMD21_TCC_CTRLA_PRESYNC       12
+#define  SAMD21_TCC_CTRLA_PRESYNC_GCLK         0
+#define  SAMD21_TCC_CTRLA_PRESYNC_PRESC                1
+#define  SAMD21_TCC_CTRLA_PRESYNC_RESYNC       2
+#define SAMD21_TCC_CTRLA_ALOCK         14
+#define SAMD21_TCC_CTRLA_CPTEN(n)      (24 + (n))
+
+#define SAMD21_TCC_CTRLB_DIR           0
+#define SAMD21_TCC_CTRLB_LUPD          1
+#define SAMD21_TCC_CTRLB_ONESHOT       2
+#define SAMD21_TCC_CTRLB_IDXCMD                3
+#define  SAMD21_TCC_CTRLB_IDXCMD_DISABLE       0
+#define  SAMD21_TCC_CTRLB_IDXCMD_SET           1
+#define  SAMD21_TCC_CTRLB_IDXCMD_CLEAR         2
+#define  SAMD21_TCC_CTRLB_IDXCMD_HOLD          3
+#define SAMD21_TCC_CTRLB_CMD           5
+#define  SAMD21_TCC_CTRLB_CMD_NONE             0
+#define  SAMD21_TCC_CTRLB_CMD_RETRIGGER                1
+#define  SAMD21_TCC_CTRLB_CMD_STOP             2
+#define  SAMD21_TCC_CTRLB_CMD_UPDATE           3
+#define  SAMD21_TCC_CTRLB_CMD_READSYNC         4
+#define  SAMD21_TCC_CTRLB_CMD_DMAOS            5
+
+#define SAMD21_TCC_SYNCBUSY_SWRST      0
+#define SAMD21_TCC_SYNCBUSY_ENABLE     1
+#define SAMD21_TCC_SYNCBUSY_CTRLB      2
+#define SAMD21_TCC_SYNCBUSY_STATUS     3
+#define SAMD21_TCC_SYNCBUSY_COUNT      4
+#define SAMD21_TCC_SYNCBUSY_PATT       5
+#define SAMD21_TCC_SYNCBUSY_WAVE       6
+#define SAMD21_TCC_SYNCBUSY_PER                7
+#define SAMD21_TCC_SYNCBUSY_CC(x)      (8 + (x))
+#define SAMD21_TCC_SYNCBUSY_PATTB      16
+#define SAMD21_TCC_SYNCBUSY_WAVEB      17
+#define SAMD21_TCC_SYNCBUSY_PERB       18
+#define SAMD21_TCC_SYNCBUSY_CCB(x)     ((19 + (x))
+
+#define SAMD21_TCC_DBGCTRL_FDDBD       2
+#define SAMD21_TCC_DBGCTRL_DBGRUN      0
+
+#define SAMD21_TCC_EVCTRL_EVACTO       0
+#define SAMD21_TCC_EVCTRL_EVACT1       3
+#define SAMD21_TCC_EVCTRL_CNTSEL       6
+#define SAMD21_TCC_EVCTRL_OVFEO                8
+#define SAMD21_TCC_EVCTRL_TRGEO                9
+#define SAMD21_TCC_EVCTRL_CNTEO                10
+#define SAMD21_TCC_EVCTRL_TCINV(x)     (12 + (x))
+#define SAMD21_TCC_EVCTRL_MCEI(x)      (16 + (x))
+#define SAMD21_TCC_EVCTRL_MCEO(x)      (24 + (x))
+
+#define SAMD21_TCC_INTFLAG_OVF         0
+#define SAMD21_TCC_INTFLAG_TRG         1
+#define SAMD21_TCC_INTFLAG_CNT         2
+#define SAMD21_TCC_INTFLAG_ERR         3
+#define SAMD21_TCC_INTFLAG_UFS         10
+#define SAMD21_TCC_INTFLAG_DFS         11
+#define SAMD21_TCC_INTFLAG_FAULTA      12
+#define SAMD21_TCC_INTFLAG_FAULTB      13
+#define SAMD21_TCC_INTFLAG_FAULT0      14
+#define SAMD21_TCC_INTFLAG_FAULT1      15
+#define SAMD21_TCC_INTFLAG_MC(x)       (16 + (x))
+
+#define SAMD21_TCC_WAVE_WAVEGEN                0
+#define  SAMD21_TCC_WAVE_WAVEGEN_NFRQ          0
+#define  SAMD21_TCC_WAVE_WAVEGEN_MFRQ          1
+#define  SAMD21_TCC_WAVE_WAVEGEN_NPWM          2
+#define  SAMD21_TCC_WAVE_WAVEGEN_DSCRITICAL    4
+#define  SAMD21_TCC_WAVE_WAVEGEN_DSBOTTOM      5
+#define  SAMD21_TCC_WAVE_WAVEGEN_DSBOTH                6
+#define  SAMD21_TCC_WAVE_WAVEGEN_DSTOP         7
+#define SAMD21_TCC_WAVE_RAMP           4
+#define SAMD21_TCC_WAVE_CIPEREN                7
+#define SAMD21_TCC_WAVE_CCCEN(x)       (8 + (x))
+#define SAMD21_TCC_WAVE_POL(x)         (16 + (x))
+#define SAMD21_TCC_WAVE_SWAP(x)                (24 + (x))
+
+/* USB */
+
+struct samd21_usb {
+       vuint8_t        ctrla;
+       vuint8_t        reserved_01;
+       vuint8_t        syncbusy;
+       vuint8_t        qosctrl;
+
+       vuint32_t       reserved_04;
+       vuint16_t       ctrlb;
+       vuint8_t        dadd;
+       vuint8_t        reserved_0b;
+       vuint8_t        status;
+       vuint8_t        fsmstatus;
+       vuint16_t       reserved_0e;
+
+       vuint16_t       fnum;
+       vuint16_t       reserved_12;
+       vuint16_t       intenclr;
+       vuint16_t       reserved_16;
+       vuint16_t       intenset;
+       vuint16_t       reserved_1a;
+       vuint16_t       intflag;
+       vuint16_t       reserved_1e;
+
+       vuint16_t       epintsmry;
+       vuint16_t       reserved_22;
+
+       vuint32_t       descadd;
+       vuint16_t       padcal;
+       uint8_t         reserved_2a[0x100 - 0x2a];
+
+       struct {
+               vuint8_t        epcfg;
+               vuint8_t        reserved_01;
+               vuint8_t        reserved_02;
+               vuint8_t        binterval;
+               vuint8_t        epstatusclr;
+               vuint8_t        epstatusset;
+               vuint8_t        epstatus;
+               vuint8_t        epintflag;
+               vuint8_t        epintenclr;
+               vuint8_t        epintenset;
+               vuint8_t        reserved_0a[0x20 - 0x0a];
+       } ep[8];
+};
+
+extern struct samd21_usb samd21_usb;
+
+#define samd21_usb (*(struct samd21_usb *) 0x41005000)
+
+#define SAMD21_USB_CTRLA_SWRST         0
+#define SAMD21_USB_CTRLA_ENABLE                1
+#define SAMD21_USB_CTRLA_RUNSTDBY      2
+#define SAMD21_USB_CTRLA_MODE          7
+
+#define SAMD21_USB_SYNCBUSY_SWRST      0
+#define SAMD21_USB_SYNCBUSY_ENABLE     1
+
+#define SAMD21_USB_QOSCTRL_CQOS                0
+#define SAMD21_USB_QOSCTRL_DQOS                2
+
+#define SAMD21_USB_CTRLB_DETACH                0
+#define SAMD21_USB_CTRLB_UPRSM         1
+#define SAMD21_USB_CTRLB_SPDCONF       2
+#define  SAMD21_USB_CTRLB_SPDCONF_FS           0
+#define  SAMD21_USB_CTRLB_SPDCONF_LS           1
+#define  SAMD21_USB_CTRLB_SPDCONF_MASK         0x3
+#define SAMD21_USB_CTRLB_NREPLY                4
+#define SAMD21_USB_CTRLB_GNAK          9
+#define SAMD21_USB_CTRLB_LPMHDSK       10
+#define  SAMD21_USB_CTRLB_LPMHDSK_NONE         0
+#define  SAMD21_USB_CTRLB_LPMHDSK_ACK          1
+#define  SAMD21_USB_CTRLB_LPMHDSK_NYET         2
+#define  SAMD21_USB_CTRLB_LPMHDSK_MASK         3
+
+#define SAMD21_USB_DADD_DADD           0
+#define SAMD21_USB_DADD_ADDEN          7
+
+#define SAMD21_USB_STATUS_SPEED                2
+#define SAMD21_USB_STATUS_LINESTATE    6
+#define SAMD21_USB_FNUM_MFNUM          0
+#define SAMD21_USB_FNUM_FNUM           3
+#define SAMD21_USB_FNUM_FNCERR         15
+#define SAMD21_USB_INTFLAG_SUSPEND     0
+#define SAMD21_USB_INTFLAG_SOF         2
+#define SAMD21_USB_INTFLAG_EORST       3
+#define SAMD21_USB_INTFLAG_WAKEUP      4
+#define SAMD21_USB_INTFLAG_EORSM       5
+#define SAMD21_USB_INTFLAG_UPRSM       6
+#define SAMD21_USB_INTFLAG_RAMACER     7
+#define SAMD21_USB_INTFLAG_LPMNYET     8
+#define SAMD21_USB_INTFLAG_LPMSUSP     9
+
+#define SAMD21_USB_PADCAL_TRANSP       0
+#define SAMD21_USB_PADCAL_TRANSN       6
+#define SAMD21_USB_PADCAL_TRIM         12
+
+#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT                0
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DISABLED      0
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_CONTROL       1
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_ISOCHRONOUS   2
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_BULK          3
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_INTERRUPT     4
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DUAL_BANK     5
+#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN         4
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_IN_DISABLED       0
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_IN_CONTROL        1
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_IN_ISOCHRONOUS    2
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_IN_BULK           3
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_IN_INTERRUPT      4
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_IN_DUAL_BANK      5
+
+#define SAMD21_USB_EP_EPSTATUS_DTGLOUT                 0
+#define SAMD21_USB_EP_EPSTATUS_DTGLIN                  1
+#define SAMD21_USB_EP_EPSTATUS_CURBK                   2
+#define SAMD21_USB_EP_EPSTATUS_STALLRQ0                        4
+#define SAMD21_USB_EP_EPSTATUS_STALLRQ1                        5
+#define SAMD21_USB_EP_EPSTATUS_BK0RDY                  6
+#define SAMD21_USB_EP_EPSTATUS_BK1RDY                  7
+
+#define SAMD21_USB_EP_EPINTFLAG_TRCPT0                 0
+#define SAMD21_USB_EP_EPINTFLAG_TRCPT1                 1
+#define SAMD21_USB_EP_EPINTFLAG_TRFAIL0                        2
+#define SAMD21_USB_EP_EPINTFLAG_TRFAIL1                        3
+#define SAMD21_USB_EP_EPINTFLAG_RXSTP                  4
+#define SAMD21_USB_EP_EPINTFLAG_STALL                  5
+
+struct samd21_usb_desc_bank {
+       vuint32_t       addr;
+       vuint32_t       pcksize;
+       vuint16_t       extreg;
+       vuint8_t        status_bk;
+       vuint8_t        reserved_0b;
+       vuint32_t       reserved_0c;
+};
+
+struct samd21_usb_desc {
+       struct samd21_usb_desc_bank bank[2];
+};
+
+extern struct samd21_usb_desc  samd21_usb_desc[8];
+
+#define SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT             0
+#define  SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT_MASK               0x3fffU
+#define SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE      14
+#define  SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE_MASK                0x3fffU
+#define SAMD21_USB_DESC_PCKSIZE_SIZE                   28
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_8                                0
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_16                       1
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_32                       2
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_64                       3
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_128                      4
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_256                      5
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_512                      6
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_1023                     7
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_MASK                     7U
+#define SAMD21_USB_DESC_PCKSIZE_AUTO_ZLP               31
+
+static inline uint16_t
+samd21_usb_desc_get_byte_count(uint8_t ep, uint8_t bank)
+{
+       return ((samd21_usb_desc[ep].bank[bank].pcksize >> SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT) &
+               SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT_MASK);
+}
+
+static inline void
+samd21_usb_desc_set_byte_count(uint8_t ep, uint8_t bank, uint32_t count)
+{
+       uint32_t pcksize = samd21_usb_desc[ep].bank[bank].pcksize;
+
+       pcksize &= ~(SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT_MASK << SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT);
+       pcksize &= ~(SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE_MASK << SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE);
+       pcksize |= (count << SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT);
+       samd21_usb_desc[ep].bank[bank].pcksize = pcksize;
+}
+
+static inline void
+samd21_usb_desc_set_size(uint8_t ep, uint8_t bank, uint32_t size)
+{
+       uint32_t pcksize = samd21_usb_desc[ep].bank[bank].pcksize;
+
+       pcksize &= ~(SAMD21_USB_DESC_PCKSIZE_SIZE_MASK << SAMD21_USB_DESC_PCKSIZE_SIZE);
+
+       uint32_t size_bits = 0;
+       switch (size) {
+       case 8: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_8; break;
+       case 16: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_16; break;
+       case 32: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_32; break;
+       case 64: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_64; break;
+       case 128: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_128; break;
+       case 256: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_256; break;
+       case 512: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_512; break;
+       case 1023: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_1023; break;
+       }
+       pcksize |= (size_bits << SAMD21_USB_DESC_PCKSIZE_SIZE);
+       samd21_usb_desc[ep].bank[bank].pcksize = pcksize;
+}
+
+static inline void
+samd21_usb_ep_set_ready(uint8_t ep, uint8_t bank)
+{
+       samd21_usb.ep[ep].epstatusset = (1 << (SAMD21_USB_EP_EPSTATUS_BK0RDY + bank));
+       samd21_usb.ep[ep].epintflag = (1 << (SAMD21_USB_EP_EPINTFLAG_TRFAIL0 + bank));
+}
+
+static inline void
+samd21_usb_ep_clr_ready(uint8_t ep, uint8_t bank)
+{
+       samd21_usb.ep[ep].epstatusclr = (1 << (SAMD21_USB_EP_EPSTATUS_BK0RDY + bank));
+}
+
+static inline uint8_t
+samd21_usb_ep_ready(uint8_t ep)
+{
+       return (samd21_usb.ep[ep].epstatus >> SAMD21_USB_EP_EPSTATUS_BK0RDY) & 3;
+}
+
+static inline uint8_t
+samd21_usb_ep_curbk(uint8_t ep)
+{
+       return (samd21_usb.ep[ep].epstatus >> SAMD21_USB_EP_EPSTATUS_CURBK) & 1;
+}
+
+/* evsys */
+
+struct samd21_evsys {
+       vuint8_t        ctrl;
+       vuint8_t        reserved_01;
+       vuint16_t       reserved_02;
+       vuint32_t       channel;
+       vuint16_t       user;
+       vuint16_t       reserved_0a;
+       vuint32_t       chstatus;
+
+       vuint32_t       intenclr;
+       vuint32_t       intenset;
+       vuint32_t       intflag;
+};
+
+extern struct samd21_evsys samd21_evsys;
+
+#define SAMD21_NUM_EVSYS       16
+
+#define samd21_evsys   (*(struct samd21_evsys *) 0x42000400)
+
+#define SAMD21_EVSYS_CONTROL_SWRST     0
+#define SAMD21_EVSYS_CONTROL_GCLKREQ   4
+
+#define SAMD21_EVSYS_CHANNEL_CHANNEL   0
+
+#define SAMD21_EVSYS_CHANNEL_SWEVT     8
+
+#define SAMD21_EVSYS_CHANNEL_EVGEN     16
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_NONE               0x00
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_RTC_CMP(i)         (0x01 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_OVF                        0x03
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_PER(i)             (0x04 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_EXTINT(i)          (0x0c + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_DMAC_CH(i)         (0x1e + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC0_OVF           0x22
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC0_TRG           0x23
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC0_CNT           0x29
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC0_MCX(i)                (0x25 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC1_OVF           0x29
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC1_TRG           0x2a
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC1_CNT           0x2b
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC1_MCX(i)                (0x2c + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC2_OVF           0x2e
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC2_TRG           0x2f
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC2_CNT           0x30
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC2_MCX(i)                (0x31 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC3_OVF            0x33
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC3_MC(i)          (0x34 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC4_OVF            0x36
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC4_MC(i)          (0x37 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC5_OVF            0x39
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC5_MC(i)          (0x3a + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC6_OVF            0x3c
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC6_MC(i)          (0x3d + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC7_OVF            0x3f
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC7_MC(i)          (0x40 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_ADC_RESRDY         0x42
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_ADC_WINMON         0x43
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_AC_COMP0           0x44
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_AC_COMP1           0x45
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_AC_WIN0            0x46
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_DAC_EMPTY          0x47
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_PTC_EOC            0x48
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_PTC_WCOMP          0x49
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_AC_COMP2           0x4a
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_AC_COMP3           0x4b
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_AC_WIN1            0x4c
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC3_OVF           0x4d
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC3_TRG           0x4e
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC3_CNT           0x4f
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC3_MCX(i)                (0x50 + (i))
+
+#define SAMD21_EVSYS_CHANNEL_PATH      24
+#define  SAMD21_EVSYS_CHANNEL_PATH_SYNCHRONOUS         0
+#define  SAMD21_EVSYS_CHANNEL_PATH_RESYNCHRONIZED      1
+#define  SAMD21_EVSYS_CHANNEL_PATH_ASYNCHRONOUS                2
+
+#define SAMD21_EVSYS_CHANNEL_EDGESEL   26
+#define  SAMD21_EVSYS_CHANNEL_EDGESEL_NO_EVT_OUTPUT    0
+#define  SAMD21_EVSYS_CHANNEL_EDGESEL_RISING_EDGE      1
+#define  SAMD21_EVSYS_CHANNEL_EDGESEL_FALLING_EDGE     2
+#define  SAMD21_EVSYS_CHANNEL_EDGESEL_BOTH_EDGES       3
+
+#define SAMD21_EVSYS_USER_USER         0
+#define  SAMD21_EVSYS_USER_USER_DMAC_CH(n)     (0x00 + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC0_EV(n)     (0x04 + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC0_MC(n)     (0x06 + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC1_EV(n)     (0x0a + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC1_MC(n)     (0x0c + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC2_EV(n)     (0x0e + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC2_MC(n)     (0x10 + (n))
+#define  SAMD21_EVSYS_USER_USER_TC(n)          (0x12 + (n))
+#define  SAMD21_EVSYS_USER_USER_ADC_START      (0x17)
+#define  SAMD21_EVSYS_USER_USER_ADC_SYNC       0x18
+#define  SAMD21_EVSYS_USER_USER_AC_COMP0       0x19
+#define  SAMD21_EVSYS_USER_USER_AC_COMP1       0x1a
+#define  SAMD21_EVSYS_USER_USER_DAC_START      0x1b
+#define  SAMD21_EVSYS_USER_USER_PTC_STCONV     0x1c
+#define  SAMD21_EVSYS_USER_USER_AC_COMP2       0x1d
+#define  SAMD21_EVSYS_USER_USER_AC_COMP3       0x1e
+#define  SAMD21_EVSYS_USER_USER_TCC3_EV(n)     (0x1f + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC3_MC(n)     (0x21 + (n))
+
+#define SAMD21_EVSYS_USER_CHANNEL      8
+#define  SAMD21_EVSYS_USER_CHANNEL_NONE                0
+#define  SAMD21_EVSYS_USER_CHANNEL_NUM(n)      ((n) + 1)
+
+
+#define SAMD21_EVSYS_CHSTATUS_USRRDY(n)        (((n) & 7) | (((n) & 8) << 1))
+#define SAMD21_EVSYS_CHSTATUS_CHBUSY(n)        (((n) & 7) | (((n) & 8) << 1) | 8)
+
+/* sercom */
+
+struct samd21_sercom {
+       vuint32_t       ctrla;
+       vuint32_t       ctrlb;
+       vuint32_t       reserved_08;
+       vuint16_t       baud;
+       vuint8_t        rxpl;
+       vuint8_t        reserved_0f;
+
+       vuint32_t       reserved_10;
+       vuint8_t        intenclr;
+       vuint8_t        reserved_15;
+       vuint8_t        intenset;
+       vuint8_t        reserved_17;
+       vuint8_t        intflag;
+       vuint8_t        reserved_19;
+       vuint16_t       status;
+       vuint32_t       syncbusy;
+
+       vuint32_t       reserved_20;
+       vuint32_t       addr;
+       vuint16_t       data;
+       vuint16_t       reserved_2a;
+       vuint32_t       reserved_2c;
+
+       vuint8_t        dbgctrl;
+       vuint8_t        reserved_31;
+       vuint16_t       reserved_32;
+       vuint16_t       fifospace;
+       vuint16_t       fifoptr;
+};
+
+extern struct samd21_sercom samd21_sercom0;
+extern struct samd21_sercom samd21_sercom1;
+extern struct samd21_sercom samd21_sercom2;
+extern struct samd21_sercom samd21_sercom3;
+extern struct samd21_sercom samd21_sercom4;
+extern struct samd21_sercom samd21_sercom5;
+
+#define SAMD21_NUM_SERCOM      6
+
+#define samd21_sercom0 (*(struct samd21_sercom *) 0x42000800)
+#define samd21_sercom1 (*(struct samd21_sercom *) 0x42000c00)
+#define samd21_sercom2 (*(struct samd21_sercom *) 0x42001000)
+#define samd21_sercom3 (*(struct samd21_sercom *) 0x42001400)
+#define samd21_sercom4 (*(struct samd21_sercom *) 0x42001800)
+#define samd21_sercom5 (*(struct samd21_sercom *) 0x42001c00)
+
+#define SAMD21_SERCOM_CTRLA_SWRST      0
+#define SAMD21_SERCOM_CTRLA_ENABLE     1
+#define SAMD21_SERCOM_CTRLA_MODE       2
+# define SAMD21_SERCOM_CTRLA_MODE_USART                1
+# define SAMD21_SERCOM_CTRLA_MODE_SPI_CLIENT   2
+# define SAMD21_SERCOM_CTRLA_MODE_SPI_HOST     3
+# define SAMD21_SERCOM_CTRLA_MODE_I2C_CLIENT   4
+# define SAMD21_SERCOM_CTRLA_MODE_I2C_HOST     5
+
+#define SAMD21_SERCOM_CTRLA_RUNSTDBY   7
+
+/* USART mode */
+#define SAMD21_SERCOM_CTRLA_IBON       8
+#define SAMD21_SERCOM_CTRLA_SAMPR      13
+#define SAMD21_SERCOM_CTRLA_TXPO       16
+#define  SAMD21_SERCOM_CTRLA_TXPO_TX_0                 0
+#define  SAMD21_SERCOM_CTRLA_TXPO_TX_2                 1
+#define  SAMD21_SERCOM_CTRLA_TXPO_TX_0_RTS_2_CTS_3     2
+#define SAMD21_SERCOM_CTRLA_RXPO       20
+#define  SAMD21_SERCOM_CTRLA_RXPO_RX_0                 0
+#define  SAMD21_SERCOM_CTRLA_RXPO_RX_1                 1
+#define  SAMD21_SERCOM_CTRLA_RXPO_RX_2                 2
+#define  SAMD21_SERCOM_CTRLA_RXPO_RX_3                 3
+#define SAMD21_SERCOM_CTRLA_SAMPA      22
+#define SAMD21_SERCOM_CTRLA_FORM       24
+#define SAMD21_SERCOM_CTRLA_CMODE      28
+#define SAMD21_SERCOM_CTRLA_CPOL       29
+#define SAMD21_SERCOM_CTRLA_DORD       30
+
+/* I2C controller mode */
+#define SAMD21_SERCOM_CTRLA_PINOUT     16
+#define SAMD21_SERCOM_CTRLA_SDAHOLD    20
+#define  SAMD21_SERCOM_CTRLA_SDAHOLD_DIS       0
+#define  SAMD21_SERCOM_CTRLA_SDAHOLD_75NS      1
+#define  SAMD21_SERCOM_CTRLA_SDAHOLD_450NS     2
+#define  SAMD21_SERCOM_CTRLA_SDAHOLD_600NS     3
+#define SAMD21_SERCOM_CTRLA_MEXTTOEN   22
+#define SAMD21_SERCOM_CTRLA_SEXTTOEN   23
+#define SAMD21_SERCOM_CTRLA_SPEED      24
+#define  SAMD21_SERCOM_CTRLA_SPEED_STANDARD    0
+#define  SAMD21_SERCOM_CTRLA_SPEED_FAST                1
+#define  SAMD21_SERCOM_CTRLA_SPEED_HIGH                2
+#define SAMD21_SERCOM_CTRLA_SCLSM      27
+#define SAMD21_SERCOM_CTRLA_INACTOUT   28
+#define  SAMD21_SERCOM_CTRLA_INACTOUT_DIS      0
+#define  SAMD21_SERCOM_CTRLA_INACTOUT_55US     1
+#define  SAMD21_SERCOM_CTRLA_INACTOUT_105US    2
+#define  SAMD21_SERCOM_CTRLA_INACTOUT_205US    3
+#define SAMD21_SERCOM_CTRLA_LOWTOUT    30
+
+/* SPI controller mode */
+#define SAMD21_SERCOM_CTRLA_DOPO       16
+#define  SAMD21_SERCOM_CTRLA_DOPO_MOSI_0_SCLK_1        0UL
+#define  SAMD21_SERCOM_CTRLA_DOPO_MOSI_2_SCLK_3        1UL
+#define  SAMD21_SERCOM_CTRLA_DOPO_MOSI_3_SCLK_1        2UL
+#define  SAMD21_SERCOM_CTRLA_DOPO_MOSI_0_SCLK_3        3UL
+#define  SAMD21_SERCOM_CTRLA_DOPO_MASK         3UL
+
+#define SAMD21_SERCOM_CTRLA_DIPO       20
+#define  SAMD21_SERCOM_CTRLA_DIPO_MISO_0       0UL
+#define  SAMD21_SERCOM_CTRLA_DIPO_MISO_1       1UL
+#define  SAMD21_SERCOM_CTRLA_DIPO_MISO_2       2UL
+#define  SAMD21_SERCOM_CTRLA_DIPO_MISO_3       3UL
+#define  SAMD21_SERCOM_CTRLA_DIPO_MASK         3UL
+
+#define SAMD21_SERCOM_CTRLA_FORM       24
+#define SAMD21_SERCOM_CTRLA_CPHA       28
+#define SAMD21_SERCOM_CTRLA_CPOL       29
+#define SAMD21_SERCOM_CTRLA_DORD       30
+#define  SAMD21_SERCOM_CTRLA_DORD_LSB  1
+#define  SAMD21_SERCOM_CTRLA_DORD_MSB  0
+
+/* USART mode */
+#define SAMD21_SERCOM_CTRLB_CHSIZE     0
+#define SAMD21_SERCOM_CTRLB_SBMODE     6
+#define SAMD21_SERCOM_CTRLB_COLDEN     8
+#define SAMD21_SERCOM_CTRLB_SFDE       9
+#define SAMD21_SERCOM_CTRLB_ENC                10
+#define SAMD21_SERCOM_CTRLB_PMODE      13
+#define SAMD21_SERCOM_CTRLB_TXEN       16
+#define SAMD21_SERCOM_CTRLB_RXEN       17
+#define SAMD21_SERCOM_CTRLB_FIFOCLR    22
+
+/* I2C mode */
+#define SAMD21_SERCOM_CTRLB_SMEN       8
+#define SAMD21_SERCOM_CTRLB_QCEN       9
+#define SAMD21_SERCOM_CTRLB_CMD                16
+#define  SAMD21_SERCOM_CTRLB_CMD_NOP           0
+#define  SAMD21_SERCOM_CTRLB_CMD_START         1
+#define  SAMD21_SERCOM_CTRLB_CMD_READ          2
+#define  SAMD21_SERCOM_CTRLB_CMD_STOP          3
+#define SAMD21_SERCOM_CTRLB_ACKACT     18
+#define  SAMD21_SERCOM_CTRLB_ACKACT_ACK                0
+#define  SAMD21_SERCOM_CTRLB_ACKACT_NACK       1
+#define SAMD21_SERCOM_CTRLB_FIFOCLR    22
+
+/* SPI mode */
+#define SAMD21_SERCOM_CTRLB_CHSIZE     0
+# define SAMD21_SERCOM_CTRLB_CHSIZE_8          0
+#define SAMD21_SERCOM_CTRLB_PLOADEN    6
+#define SAMD21_SERCOM_CTRLB_SSDE       9
+#define SAMD21_SERCOM_CTRLB_MSSEN      13
+#define SAMD21_SERCOM_CTRLB_AMODE      14
+#define SAMD21_SERCOM_CTRLB_RXEN       17
+
+/* USART mode */
+#define SAMD21_SERCOM_INTFLAG_DRE      0
+#define SAMD21_SERCOM_INTFLAG_TXC      1
+#define SAMD21_SERCOM_INTFLAG_RXC      2
+#define SAMD21_SERCOM_INTFLAG_RXS      3
+#define SAMD21_SERCOM_INTFLAG_CTSIC    4
+#define SAMD21_SERCOM_INTFLAG_RXBRK    5
+#define SAMD21_SERCOM_INTFLAG_ERROR    7
+
+/* I2C mode */
+#define SAMD21_SERCOM_INTFLAG_ERROR    7
+#define SAMD21_SERCOM_INTFLAG_RXFF     4
+#define SAMD21_SERCOM_INTFLAG_TXFE     3
+#define SAMD21_SERCOM_INTFLAG_SB       1
+#define SAMD21_SERCOM_INTFLAG_MB       0
+
+/* SPI mode */
+#define SAMD21_SERCOM_INTFLAG_SSL      3
+
+#define SAMD21_SERCOM_INTENCLR_DRE     0
+#define SAMD21_SERCOM_INTENCLR_TXC     1
+#define SAMD21_SERCOM_INTENCLR_RXC     2
+#define SAMD21_SERCOM_INTENCLR_RXS     3
+#define SAMD21_SERCOM_INTENCLR_CTSIC   4
+#define SAMD21_SERCOM_INTENCLR_RXBRK   5
+#define SAMD21_SERCOM_INTENCLR_ERROR   7
+
+#define SAMD21_SERCOM_STATUS_PERR      0
+#define SAMD21_SERCOM_STATUS_FERR      1
+#define SAMD21_SERCOM_STATUS_BUFOVF    2
+#define SAMD21_SERCOM_STATUS_CTS       3
+#define SAMD21_SERCOM_STATUS_ISF       4
+#define SAMD21_SERCOM_STATUS_COLL      5
+#define SAMD21_SERCOM_STATUS_TXE       6
+
+#define SAMD21_SERCOM_SYNCBUSY_SWRST   0
+#define SAMD21_SERCOM_SYNCBUSY_ENABLE  1
+#define SAMD21_SERCOM_SYNCBUSY_CTRLB   2
+#define SAMD21_SERCOM_SYNCBUSY_SYSOP   2
+
+#define SAMD21_SERCOM_ADDR_ADDR                0
+#define SAMD21_SERCOM_ADDR_LENEN       13
+#define SAMD21_SERCOM_ADDR_HS          14
+#define SAMD21_SERCOM_ADDR_TENBITEN    15
+#define SAMD21_SERCOM_ADDR_LEN         16
+
+#define SAMD21_SERCOM_DBGCTRL_DBGSTOP  0
+
+#define SAMD21_SERCOM_FIFOSPACE_TXSPACE        0
+#define  SAMD21_SERCOM_FIFOSPACE_TXSPACE_MASK  0x1f
+#define SAMD21_SERCOM_FIFOSPACE_RXSPACE        8
+#define  SAMD21_SERCOM_FIFOSPACE_RXSPACE_MASK  0x1f
+
+#define SAMD21_SERCOM_FIFOPTR_CPUWRPTR 0
+#define  SAMD21_SERCOM_FIFOPTR_CPUWRPTR_MASK   0xf
+#define SAMD21_SERCOM_FIFOPTR_CPURDPTR 8
+#define  SAMD21_SERCOM_FIFOPTR_CPURDPTR_MASK   0xf
+
+/* The SYSTICK starts at 0xe000e010 */
+struct samd21_systick {
+       vuint32_t       csr;
+       vuint32_t       rvr;
+       vuint32_t       cvr;
+       vuint32_t       calib;
+};
+
+extern struct samd21_systick samd21_systick;
+
+#define samd21_systick (*(struct samd21_systick *) 0xe000e010)
+
+#define SAMD21_SYSTICK_CSR_ENABLE      0
+#define SAMD21_SYSTICK_CSR_TICKINT     1
+#define SAMD21_SYSTICK_CSR_CLKSOURCE   2
+#define  SAMD21_SYSTICK_CSR_CLKSOURCE_EXTERNAL         0
+#define  SAMD21_SYSTICK_CSR_CLKSOURCE_HCLK_8           1
+#define SAMD21_SYSTICK_CSR_COUNTFLAG   16
+
+#define SAMD21_SYSTICK_PRI             15
+
+/* The NVIC starts at 0xe000e100, so add that to the offsets to find the absolute address */
+
+struct samd21_nvic {
+       vuint32_t       iser;           /* 0x000 0xe000e100 Set Enable Register */
+
+       uint8_t         _unused020[0x080 - 0x004];
+
+       vuint32_t       icer;           /* 0x080 0xe000e180 Clear Enable Register */
+
+       uint8_t         _unused0a0[0x100 - 0x084];
+
+       vuint32_t       ispr;           /* 0x100 0xe000e200 Set Pending Register */
+
+       uint8_t         _unused120[0x180 - 0x104];
+
+       vuint32_t       icpr;           /* 0x180 0xe000e280 Clear Pending Register */
+
+       uint8_t         _unused1a0[0x300 - 0x184];
+
+       vuint32_t       ipr[8];         /* 0x300 0xe000e400 Priority Register */
+};
+
+extern struct samd21_nvic samd21_nvic;
+
+#define samd21_nvic (*(struct samd21_nvic *) 0xe000e100)
+
+#define SAMD21_NVIC_ISR_PM_POS         0
+#define SAMD21_NVIC_ISR_SYSCTRL_POS    1
+#define SAMD21_NVIC_ISR_WDT_POS                2
+#define SAMD21_NVIC_ISR_RTC_POS                3
+#define SAMD21_NVIC_ISR_EIC_POS                4
+#define SAMD21_NVIC_ISR_NVMCTRL_POS    5
+#define SAMD21_NVIC_ISR_DMAC_POS       6
+#define SAMD21_NVIC_ISR_USB_POS                7
+#define SAMD21_NVIC_ISR_EVSYS_POS      8
+#define SAMD21_NVIC_ISR_SERCOM0_POS    9
+#define SAMD21_NVIC_ISR_SERCOM1_POS    10
+#define SAMD21_NVIC_ISR_SERCOM2_POS    11
+#define SAMD21_NVIC_ISR_SERCOM3_POS    12
+#define SAMD21_NVIC_ISR_SERCOM4_POS    13
+#define SAMD21_NVIC_ISR_SERCOM5_POS    14
+#define SAMD21_NVIC_ISR_TCC0_POS       15
+#define SAMD21_NVIC_ISR_TCC1_POS       16
+#define SAMD21_NVIC_ISR_TCC2_POS       17
+#define SAMD21_NVIC_ISR_TC3_POS                18
+#define SAMD21_NVIC_ISR_TC4_POS                19
+#define SAMD21_NVIC_ISR_TC5_POS                20
+#define SAMD21_NVIC_ISR_TC6_POS                21
+#define SAMD21_NVIC_ISR_TC7_POS                22
+#define SAMD21_NVIC_ISR_ADC_POS                23
+#define SAMD21_NVIC_ISR_AC_POS         24
+#define SAMD21_NVIC_ISR_DAC_POS                25
+#define SAMD21_NVIC_ISR_PTC_POS                26
+#define SAMD21_NVIC_ISR_I2S_POS                27
+#define SAMD21_NVIC_ISR_AC1_POS                28
+#define SAMD21_NVIC_ISR_TCC3_POS       29
+
+#define IRQ_MASK(irq)  (1 << (irq))
+#define IRQ_BOOL(v,irq)        (((v) >> (irq)) & 1)
+
+static inline void
+samd21_nvic_set_enable(int irq) {
+       samd21_nvic.iser = IRQ_MASK(irq);
+}
+
+static inline void
+samd21_nvic_clear_enable(int irq) {
+       samd21_nvic.icer = IRQ_MASK(irq);
+}
+
+static inline int
+samd21_nvic_enabled(int irq) {
+       return IRQ_BOOL(samd21_nvic.iser, irq);
+}
+
+static inline void
+samd21_nvic_set_pending(int irq) {
+       samd21_nvic.ispr = IRQ_MASK(irq);
+}
+
+static inline void
+samd21_nvic_clear_pending(int irq) {
+       samd21_nvic.icpr = IRQ_MASK(irq);
+}
+
+static inline int
+samd21_nvic_pending(int irq) {
+       return IRQ_BOOL(samd21_nvic.ispr, irq);
+}
+
+#define IRQ_PRIO_REG(irq)      ((irq) >> 2)
+#define IRQ_PRIO_BIT(irq)      (((irq) & 3) << 3)
+#define IRQ_PRIO_MASK(irq)     (0xffU << IRQ_PRIO_BIT(irq))
+
+static inline void
+samd21_nvic_set_priority(int irq, uint8_t prio) {
+       int             n = IRQ_PRIO_REG(irq);
+       uint32_t        v;
+
+       v = samd21_nvic.ipr[n];
+       v &= ~IRQ_PRIO_MASK(irq);
+       v |= (prio) << IRQ_PRIO_BIT(irq);
+       samd21_nvic.ipr[n] = v;
+}
+
+static inline uint8_t
+samd21_nvic_get_priority(int irq) {
+       return (samd21_nvic.ipr[IRQ_PRIO_REG(irq)] >> IRQ_PRIO_BIT(irq)) & IRQ_PRIO_MASK(0);
+}
+
+
+
+/* Cortex M0+ SCB */
+
+struct samd21_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       mmfar;
+       vuint32_t       bfar;
+};
+
+extern struct samd21_scb samd21_scb;
+
+#define samd21_scb (*(struct samd21_scb *) 0xe000ed00)
+
+#define SAMD21_SCB_AIRCR_VECTKEY       16
+#define  SAMD21_SCB_AIRCR_VECTKEY_KEY          0x05fa
+#define SAMD21_SCB_AIRCR_PRIGROUP      8
+#define SAMD21_SCB_AIRCR_SYSRESETREQ   2
+#define SAMD21_SCB_AIRCR_VECTCLRACTIVE 1
+#define SAMD21_SCB_AIRCR_VECTRESET     0
+
+/* The NVM Calibration and auxiliary space starts at 0x00800000 */
+
+struct samd21_aux0 {
+       vuint64_t       userrow;
+};
+
+extern struct samd21_aux0 samd21_aux0;
+
+#define samd21_aux0 (*(struct samd21_aux0 *) 0x00804000)
+
+#define SAMD21_AUX0_USERROW_BOOTPROT   0
+#define SAMD21_AUX0_USERROW_EEPROM             4
+#define SAMD21_AUX0_USERROW_BOD33_LEVEL        8
+#define SAMD21_AUX0_USERROW_BOD33_ENABLE       14
+#define SAMD21_AUX0_USERROW_BOD33_ACTION       15
+#define SAMD21_AUX0_USERROW_WDT_ENABLE 25
+#define SAMD21_AUX0_USERROW_WDT_ALWAYS_ON      26
+#define SAMD21_AUX0_USERROW_WDT_PERIOD 27
+#define SAMD21_AUX0_USERROW_WDT_WINDOW 31
+#define SAMD21_AUX0_USERROW_WDT_EWOFFSET       35
+#define SAMD21_AUX0_USERROW_WDT_WEN            39
+#define SAMD21_AUX0_USERROW_BOD33_HYST 40
+#define SAMD21_AUX0_USERROW_LOCK               48
+
+struct samd21_aux1 {
+       vuint64_t       reserved_00;
+       vuint64_t       device_config;
+
+       vuint64_t       reserved_10;
+       vuint64_t       reserved_18;
+
+       vuint64_t       calibration;
+       vuint64_t       reserved_28;
+};
+
+extern struct samd21_aux1 samd21_aux1;
+
+#define samd21_aux1 (*(struct samd21_aux1 *) 0x00806000)
+
+#define SAMD21_AUX1_CALIBRATION_ADC_LINEARITY  27
+#define  SAMD21_AUX1_CALIBRATION_ADC_LINEARITY_MASK    0xff
+#define SAMD21_AUX1_CALIBRATION_ADC_BIASCAL    35
+#define  SAMD21_AUX1_CALIBRATION_ADC_BIASCAL_MASK      0x7
+#define SAMD21_AUX1_CALIBRATION_OSC32K_CAL     38
+#define SAMD21_AUX1_CALIBRATION_USB_TRANSN     45
+#define  SAMD21_AUX1_CALIBRATION_USB_TRANSN_MASK       0x1f
+#define SAMD21_AUX1_CALIBRATION_USB_TRANSP     50
+#define  SAMD21_AUX1_CALIBRATION_USB_TRANSP_MASK       0x1f
+#define SAMD21_AUX1_CALIBRATION_USB_TRIM       55
+#define  SAMD21_AUX1_CALIBRATION_USB_TRIM_MASK         0x07
+#define SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL     58
+#define  SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL_MASK       0x3f
+
+struct samd21_serial {
+       vuint32_t       reserved_00;
+       vuint32_t       reserved_04;
+       vuint32_t       reserved_08;
+       vuint32_t       word0;
+
+       vuint32_t       reserved_10;
+       vuint32_t       reserved_14;
+       vuint32_t       reserved_18;
+       vuint32_t       reserved_1c;
+
+       vuint32_t       reserved_20;
+       vuint32_t       reserved_24;
+       vuint32_t       reserved_28;
+       vuint32_t       reserved_2c;
+
+       vuint32_t       reserved_30;
+       vuint32_t       reserved_34;
+       vuint32_t       reserved_38;
+       vuint32_t       reserved_3c;
+
+       vuint32_t       word1;
+       vuint32_t       word2;
+       vuint32_t       word3;
+       vuint32_t       reserved_4c;
+};
+
+extern struct samd21_serial samd21_serial;
+
+#define samd21_serial (*(struct samd21_serial *) 0x0080a000)
+
+static inline void
+samd21_gclk_wait_sync(void)
+{
+       while (samd21_gclk.status & (1 << SAMD21_GCLK_STATUS_SYNCBUSY))
+               ;
+}
+
+static inline void
+samd21_dfll_wait_sync(void)
+{
+       while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_DFLLRDY)) == 0)
+               ;
+}
+
+static inline void
+samd21_gclk_gendiv(uint32_t id, uint32_t div)
+{
+       if (div == 1)
+               div = 0;
+       samd21_gclk.gendiv = ((id << SAMD21_GCLK_GENDIV_ID) |
+                             (div << SAMD21_GCLK_GENDIV_DIV));
+       samd21_gclk_wait_sync();
+}
+
+static inline void
+samd21_gclk_genctrl(uint32_t src, uint32_t id)
+{
+       samd21_gclk.genctrl = ((id << SAMD21_GCLK_GENCTRL_ID) |
+                              (src << SAMD21_GCLK_GENCTRL_SRC) |
+                              (0 << SAMD21_GCLK_GENCTRL_OE) |
+                              (1 << SAMD21_GCLK_GENCTRL_GENEN));
+       samd21_gclk_wait_sync();
+}
+
+static inline void
+samd21_gclk_clkctrl(uint32_t gen, uint32_t id)
+{
+       samd21_gclk.clkctrl = (uint16_t) ((gen << SAMD21_GCLK_CLKCTRL_GEN) |
+                                         (id << SAMD21_GCLK_CLKCTRL_ID) |
+                                         (1U << SAMD21_GCLK_CLKCTRL_CLKEN));
+       samd21_gclk_wait_sync();
+}
+
+#define isr_decl(name) \
+       void samd21_ ## 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(pm);          /* IRQ0 */
+isr_decl(sysctrl);
+isr_decl(wdt);
+isr_decl(rtc);
+isr_decl(eic);
+isr_decl(nvmctrl);
+isr_decl(dmac);
+isr_decl(usb);
+isr_decl(evsys);
+isr_decl(sercom0);
+isr_decl(sercom1);
+isr_decl(sercom2);
+isr_decl(sercom3);
+isr_decl(sercom4);
+isr_decl(sercom5);
+isr_decl(tcc0);
+isr_decl(tcc1);
+isr_decl(tcc2);
+isr_decl(tc3);
+isr_decl(tc4);
+isr_decl(tc5);
+isr_decl(tc6);
+isr_decl(tc7);
+isr_decl(adc);
+isr_decl(ac);
+isr_decl(dac);
+isr_decl(ptc);
+isr_decl(i2s);
+isr_decl(ac1);
+isr_decl(tcc3);
+
+#undef isr_decl
+
+#endif /* _SAMD21_H_ */
diff --git a/src/snekboard/Makefile b/src/snekboard/Makefile
new file mode 100644 (file)
index 0000000..5d4f863
--- /dev/null
@@ -0,0 +1,71 @@
+#
+# AltOS build
+#
+#
+
+include ../samd21/Makefile.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_boot.h \
+       ao_pins.h \
+       ao_product.h \
+       ao_task.h \
+       samd21.h \
+       Makefile
+
+ALTOS_SRC = \
+       ao_boot_chain.c \
+       ao_romconfig.c \
+       ao_interrupt.c \
+       ao_product.c \
+       ao_cmd.c \
+       ao_task.c \
+       ao_led.c \
+       ao_stdio.c \
+       ao_panic.c \
+       ao_timer.c \
+       ao_mutex.c \
+       ao_dma_samd21.c \
+       ao_usb_samd21.c \
+       ao_spi_samd21.c
+
+# SAMD21G18A
+
+SAMD21_ROM=256
+SAMD21_RAM=32
+
+PRODUCT=SnekBoard
+PRODUCT_DEF=-DSNEKBOARD
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(SAMD21_CFLAGS)
+
+PROGNAME=snekboard
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) snekboard.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) 
+       $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+load: $(PROG)
+       stm-load $(PROG)
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/snekboard/ao_pins.h b/src/snekboard/ao_pins.h
new file mode 100644 (file)
index 0000000..80b2cb7
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define LED_0_PORT     (&samd21_port_a)
+#define LED_0_PIN      2
+
+#define LED_BLUE       (1 << 0)
+
+#define AO_LED_PANIC   LED_BLUE
+
+#define HAS_BEEP       0
+
+#define HAS_USB                1
+#define USE_USB_STDIO  1
+
+#define HAS_LED                1
+
+#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 HAS_SPI_0              1
+#define SPI_0_PA08_PA09_PA10   1
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/snekboard/flash-loader/Makefile b/src/snekboard/flash-loader/Makefile
new file mode 100644 (file)
index 0000000..12330d3
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=snekboard
+include $(TOPDIR)/samd21/Makefile-flash.defs
diff --git a/src/snekboard/flash-loader/ao_pins.h b/src/snekboard/flash-loader/ao_pins.h
new file mode 100644 (file)
index 0000000..fb39981
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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_
+
+#include <ao_flash_samd21_pins.h>
+
+/* ANALOG1 to gnd for boot loader mode */
+
+#define AO_BOOT_PIN                    1
+#define AO_BOOT_APPLICATION_GPIO       (samd21_port_b)
+#define AO_BOOT_APPLICATION_PIN                8
+#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_ */
diff --git a/src/snekboard/snekboard.c b/src/snekboard/snekboard.c
new file mode 100644 (file)
index 0000000..719ea01
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#include <ao.h>
+#include <ao_led.h>
+#include <ao_dma_samd21.h>
+
+#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)
+
+static const uint8_t spi_test[] = {
+       0x55,
+};
+
+static void
+ao_spi_test(void)
+{
+       ao_spi_get_bit(SNEK_CS_PORT, SNEK_CS_PIN, SNEK_SPI_INDEX, SNEK_SPI_SPEED);
+       ao_spi_send(spi_test, sizeof(spi_test), SNEK_SPI_INDEX);
+       ao_spi_put_bit(SNEK_CS_PORT, SNEK_CS_PIN, SNEK_SPI_INDEX);
+}
+
+const struct ao_cmds ao_spi_cmds[] = {
+       { ao_spi_test,  "s \0Send some bytes over spi" },
+       { 0, NULL },
+};
+
+int main(void)
+{
+       ao_led_init();
+       ao_clock_init();
+       ao_task_init();
+       ao_timer_init();
+       ao_dma_init();
+       ao_spi_init();
+       ao_usb_init();
+       ao_cmd_register(ao_spi_cmds);
+       ao_spi_init_cs(&samd21_port_a, 1 << 11); /* analog 8 for CS */
+       ao_cmd_init();
+       ao_start_scheduler();
+       return 0;
+}
index 9f31a36fa6f9e8bc5a58b98f46b6b1ce3a2aa038..9922513be007bf005c19c9d9d1cd5021e3266300 100644 (file)
@@ -92,6 +92,9 @@ 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);
 
index 0215f03ebd6a56bf173c16e16e8c0a53cd008955..ad8f55b5b0941f9955ad260b7c35ad144f07e40b 100644 (file)
@@ -427,6 +427,16 @@ ao_spi_put(uint8_t spi_index)
        ao_mutex_put(&ao_spi_mutex[id]);
 }
 
+void
+ao_spi_put_pins(uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+
+       ao_spi_disable_pin_config(ao_spi_pin_config[id]);
+       ao_spi_pin_config[id] = AO_SPI_CONFIG_NONE;
+       ao_spi_put(spi_index);
+}
+
 static void
 ao_spi_channel_init(uint8_t spi_index)
 {
index f03d96957802674ffc22d54b009be87805c15119..385fe0e32b192d6c815ec0fd32bdb96f8d69abe0 100644 (file)
@@ -139,8 +139,7 @@ ao_spi_recv_byte(uint8_t spi_index)
 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);
+#define AO_SPI_DUPLEX  0
 
 void
 ao_spi_init(void);
index 8a173b57e5be79e928bebb5acf6ffb0bc1fa3d72..1a9f28e30ef0b68d4f7ac69fe846e16dea915bb5 100644 (file)
@@ -95,6 +95,9 @@ 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);
 
index 0ce114cb0aeef99cffbc2297d70685bd5b1120df..ae4417a274f4b3cc1f628bbdfedfbdd047e58f60 100644 (file)
@@ -499,6 +499,16 @@ ao_spi_put(uint8_t spi_index)
        ao_mutex_put(&ao_spi_mutex[id]);
 }
 
+void
+ao_spi_put_pins(uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+
+       ao_spi_disable_pin_config(ao_spi_pin_config[id]);
+       ao_spi_pin_config[id] = AO_SPI_CONFIG_NONE;
+       ao_spi_put(spi_index);
+}
+
 static void
 ao_spi_channel_init(uint8_t spi_index)
 {
index ce2d949f34d9702995825edc2253213e7070b080..6efa407d15043010e7185198f91a62c26b28a77e 100644 (file)
@@ -178,50 +178,10 @@ struct ao_adc {
 #define AO_RN_SW_BTN_PORT      (&stm_gpioc)
 #define AO_RN_SW_BTN_PIN       14
 
-/* Pin 9. BM70 P2_3 */
-#define AO_RN_WAKEUP_PORT      (&stm_gpiob)
-#define AO_RN_WAKEUP_PIN       9
-
-/* Pin 11. BM70 P2_7/tx_ind. Status indication along with P1_5 */
-#define AO_RN_P0_4_PORT                (&stm_gpioc)
-#define AO_RN_P0_4_PIN         13
-
-/* Pin 12. BM70 P1_1. Status indication along with P0_4 */
+/* Pin 12. BM70 P1_5. Status indication along with P0_4 */
 #define AO_RN_P1_5_PORT                (&stm_gpiob)
 #define AO_RN_P1_5_PIN         6
 
-/* Pin 13. BM70 P1_2. Also I2C SCL */
-#define AO_RN_P1_2_PORT                (&stm_gpiob)
-#define AO_RN_P1_2_PIN         7
-
-/* Pin 14. BM70 P1_3. Also I2C SDA */
-#define AO_RN_P1_3_PORT                (&stm_gpiob)
-#define AO_RN_P1_3_PIN         8
-
-/* Pin 15. BM70 P0_0/cts. */
-#define AO_RN_CTS_PORT         (&stm_gpioa)
-#define AO_RN_CTS_PIN          1
-
-/* Pin 16. BM70 P1_0. */
-#define AO_RN_P0_5_PORT                (&stm_gpiob)
-#define AO_RN_P0_5_PIN         5
-
-/* Pin 17. BM70 P3_6. */
-#define AO_RN_RTS_PORT         (&stm_gpioa)
-#define AO_RN_RTS_PIN          0
-
-/* Pin 18. BM70 P2_0. */
-#define AO_RN_P2_0_PORT                (&stm_gpiob)
-#define AO_RN_P2_0_PIN         3
-
-/* Pin 19. BM70 P2_4. */
-#define AO_RN_P2_4_PORT                (&stm_gpioa)
-#define AO_RN_P2_4_PIN         10
-
-/* Pin 20. BM70 NC. */
-#define AO_RN_EAN_PORT
-#define AO_RN_EAN_PIN
-
 /* Pin 21. BM70 RST_N. */
 #define AO_RN_RST_N_PORT       (&stm_gpioa)
 #define AO_RN_RST_N_PIN                15
@@ -238,22 +198,6 @@ struct ao_adc {
 #define AO_RN_P3_1_PORT                (&stm_gpiob)
 #define AO_RN_P3_1_PIN         2
 
-/* Pin 25. BM70 P3_2/LINK_DROP. */
-#define AO_RN_P3_2_PORT                (&stm_gpioa)
-#define AO_RN_P3_2_PIN         8
-
-/* Pin 26. BM70 P3_3/UART_RX_IND. */
-#define AO_RN_P3_3_PORT                (&stm_gpiob)
-#define AO_RN_P3_3_PIN         15
-
-/* Pin 27. BM70 P3_4/PAIRING_KEY. */
-#define AO_RN_P3_4_PORT                (&stm_gpiob)
-#define AO_RN_P3_4_PIN         14
-
-/* Pin 28. BM70 P3_5. */
-#define AO_RN_P3_6_PORT                (&stm_gpiob)
-#define AO_RN_P3_6_PIN         13
-
 /* Pin 29. BM70 P0_7. */
 #define AO_RN_P3_7_PORT                (&stm_gpiob)
 #define AO_RN_P3_7_PIN         12
index 3583be98f9ec23a7586c1d4d00ce46237bd34f27..78849bc9b43bc00f551a70133aef6fbdcd66327a 100644 (file)
@@ -33,6 +33,9 @@
 
 /* UI values */
 static uint8_t ao_lco_select_mode;
+static uint8_t ao_lco_event_debug;
+
+#define PRINTE(...) do { if (!ao_lco_debug && !ao_lco_event_debug) break; printf ("\r%5lu %s: ", (unsigned long) ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
 #define AO_LCO_SELECT_PAD      0
 #define AO_LCO_SELECT_BOX      1
 
@@ -176,7 +179,7 @@ ao_lco_input(void)
 
        for (;;) {
                ao_event_get(&event);
-               PRINTD("event type %d unit %d value %ld\n",
+               PRINTE("event type %d unit %d value %ld\n",
                       event.type, event.unit, (long) event.value);
                switch (event.type) {
                case AO_EVENT_QUADRATURE:
@@ -283,13 +286,16 @@ static void
 ao_lco_set_debug(void)
 {
        uint32_t r = ao_cmd_decimal();
-       if (ao_cmd_status == ao_cmd_success)
-               ao_lco_debug = r != 0;
+       if (ao_cmd_status == ao_cmd_success){
+               ao_lco_debug = r & 1;
+               ao_lco_event_debug = (r & 2) >> 1;
+       }
 }
 
 const struct ao_cmds ao_lco_cmds[] = {
        { ao_lco_set_debug,     "D <0 off, 1 on>\0Debug" },
        { ao_lco_search,        "s\0Search for pad boxes" },
+       { ao_lco_pretend,       "p\0Pretend there are lots of pad boxes" },
        { 0, NULL }
 };
 #endif
diff --git a/src/telemega-v6.0/Makefile b/src/telemega-v6.0/Makefile
new file mode 100644 (file)
index 0000000..97970f0
--- /dev/null
@@ -0,0 +1,130 @@
+#
+# AltOS build
+#
+#
+
+include ../stm/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_ms5607.h \
+       ao_bmi088.h \
+       ao_mmc5983.h \
+       ao_adxl375.h \
+       ao_cc1200_CC1200.h \
+       ao_profile.h \
+       ao_task.h \
+       ao_whiten.h \
+       ao_sample_profile.h \
+       ao_quaternion.h \
+       ao_mpu.h \
+       stm32l.h \
+       ao_ms5607_convert.c \
+       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_product.c \
+       ao_romconfig.c \
+       ao_cmd.c \
+       ao_config.c \
+       ao_task.c \
+       ao_led_stm.c \
+       ao_stdio.c \
+       ao_panic.c \
+       ao_timer.c \
+       ao_mutex.c \
+       ao_serial_stm.c \
+       ao_gps_ublox.c \
+       ao_gps_show.c \
+       ao_gps_report_mega.c \
+       ao_ignite.c \
+       ao_freq.c \
+       ao_dma_stm.c \
+       ao_spi_stm.c \
+       ao_cc1200.c \
+       ao_data.c \
+       ao_ms5607.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_bmi088.c \
+       ao_mmc5983.c \
+       ao_convert_pa.c \
+       ao_convert_volt.c \
+       ao_log.c \
+       ao_log_mega.c \
+       ao_sample.c \
+       ao_kalman.c \
+       ao_flight.c \
+       ao_telemetry.c \
+       ao_packet_slave.c \
+       ao_packet.c \
+       ao_companion.c \
+       ao_pyro.c \
+       ao_aprs.c \
+       ao_pwm_stm.c \
+       $(PROFILE) \
+       $(SAMPLE_PROFILE) \
+       $(STACK_GUARD)
+
+PRODUCT=TeleMega-v6.0
+PRODUCT_DEF=-DTELEMEGA
+IDPRODUCT=0x0023
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF)
+
+PROGNAME=telemega-v6.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_telemega.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos.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
+
+install:
+
+uninstall:
diff --git a/src/telemega-v6.0/ao_pins.h b/src/telemega-v6.0/ao_pins.h
new file mode 100644 (file)
index 0000000..da1dd15
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+
+/* 16MHz High speed external crystal */
+#define AO_HSE                 16000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL              6
+#define AO_RCC_CFGR_PLLMUL     (STM_RCC_CFGR_PLLMUL_6)
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV              3
+#define AO_RCC_CFGR_PLLDIV     (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER       1
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER      2
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER      2
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_SERIAL_1           0
+#define USE_SERIAL_1_STDIN     0
+#define SERIAL_1_PB6_PB7       0
+#define SERIAL_1_PA9_PA10      0
+
+#define HAS_SERIAL_2           1
+#define USE_SERIAL_2_STDIN     0
+#define SERIAL_2_PA2_PA3       1
+#define SERIAL_2_PD5_PD6       0
+#define USE_SERIAL_2_FLOW      0
+#define USE_SERIAL_2_SW_FLOW   0
+
+#define HAS_SERIAL_3           0
+#define USE_SERIAL_3_STDIN     0
+#define SERIAL_3_PB10_PB11     0
+#define SERIAL_3_PC10_PC11     0
+#define SERIAL_3_PD8_PD9       0
+
+#define ao_gps_getchar         ao_serial2_getchar
+#define ao_gps_putchar         ao_serial2_putchar
+#define ao_gps_set_speed       ao_serial2_set_speed
+#define ao_gps_fifo            (ao_stm_usart2.rx_fifo)
+
+#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX       (1024 * 1024)
+#define AO_CONFIG_MAX_SIZE                     1024
+#define LOG_ERASE_MARK                         0x55
+#define LOG_MAX_ERASE                          128
+#define AO_LOG_FORMAT                          AO_LOG_FORMAT_TELEMEGA_6
+#define AO_LOG_NORMALIZED                      1
+
+#define HAS_EEPROM             1
+#define USE_INTERNAL_FLASH     0
+#define USE_EEPROM_CONFIG      1
+#define USE_STORAGE_CONFIG     0
+#define HAS_USB                        1
+#define HAS_BEEP               1
+#define BEEPER_TIMER           3
+#define BEEPER_CHANNEL         2
+#define BEEPER_PORT            (&stm_gpioe)
+#define BEEPER_PIN             4
+#define AO_BEEP_MID_DEFAULT    179             /* 2100 Hz */
+#define AO_BEEP_MAKE_LOW(m)    ((uint8_t) ((m) * 197U/179U)) /* 1900 Hz */
+#define AO_BEEP_MAKE_HIGH(m)   ((uint8_t) ((m) * 163U/179U)) /* 2300 Hz */
+#define HAS_BATTERY_REPORT     1
+#define HAS_RADIO              1
+#define HAS_TELEMETRY          1
+#define HAS_APRS               1
+#define HAS_COMPANION          1
+
+#define HAS_SPI_1              1
+#define SPI_1_PA5_PA6_PA7      1       /* Barometer */
+#define SPI_1_PB3_PB4_PB5      1       /* Accelerometer */
+#define SPI_1_PE13_PE14_PE15   1       /* MPU6000 */
+#define SPI_1_OSPEEDR          STM_OSPEEDR_10MHz
+
+//#define MMC5983_I2C          1
+
+#define HAS_SPI_2              1
+#define SPI_2_PB13_PB14_PB15   1       /* Flash, Companion */
+#ifndef MMC5983_I2C
+#define SPI_2_PD1_PD3_PD4      1       /* MMC5983 */
+#endif
+#define SPI_2_OSPEEDR          STM_OSPEEDR_10MHz
+
+#define HAS_I2C_1              0
+#define I2C_1_PB8_PB9          0
+
+#define HAS_I2C_2              0
+#define I2C_2_PB10_PB11                0
+
+#define PACKET_HAS_SLAVE       1
+#define PACKET_HAS_MASTER      0
+
+#define LOW_LEVEL_DEBUG                0
+
+#define LED_PORT_ENABLE                STM_RCC_AHBENR_GPIOCEN
+#define LED_PORT               (&stm_gpioc)
+#define LED_PIN_RED            8
+#define LED_PIN_GREEN          9
+#define AO_LED_RED             (1 << LED_PIN_RED)
+#define AO_LED_GREEN           (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE         (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_GPS                        1
+#define HAS_FLIGHT             1
+#define HAS_ADC                        1
+#define HAS_ADC_TEMP           1
+#define HAS_LOG                        1
+
+/*
+ * Igniter
+ */
+
+#define HAS_IGNITE             1
+#define HAS_IGNITE_REPORT      1
+
+#define AO_SENSE_PYRO(p,n)     ((p)->adc.sense[n])
+#define AO_SENSE_DROGUE(p)     ((p)->adc.sense[4])
+#define AO_SENSE_MAIN(p)       ((p)->adc.sense[5])
+#define AO_IGNITER_CLOSED      400
+#define AO_IGNITER_OPEN                60
+
+/* Pyro A */
+#define AO_PYRO_PORT_0 (&stm_gpiod)
+#define AO_PYRO_PIN_0  6
+
+/* Pyro B */
+#define AO_PYRO_PORT_1 (&stm_gpiod)
+#define AO_PYRO_PIN_1  7
+
+/* Pyro C */
+#define AO_PYRO_PORT_2 (&stm_gpioe)
+#define AO_PYRO_PIN_2  3
+
+/* Pyro D */
+#define AO_PYRO_PORT_3 (&stm_gpioe)
+#define AO_PYRO_PIN_3  2
+
+/* Drogue */
+#define AO_IGNITER_DROGUE_PORT (&stm_gpioe)
+#define AO_IGNITER_DROGUE_PIN  6
+
+/* Main */
+#define AO_IGNITER_MAIN_PORT   (&stm_gpioe)
+#define AO_IGNITER_MAIN_PIN    5
+
+/* Number of general purpose pyro channels available */
+#define AO_PYRO_NUM    4
+
+/*
+ * ADC
+ */
+#define AO_DATA_RING           32
+#define AO_ADC_NUM_SENSE       6
+
+struct ao_adc {
+       int16_t                 sense[AO_ADC_NUM_SENSE];
+       int16_t                 v_batt;
+       int16_t                 v_pbatt;
+       int16_t                 temp;
+};
+
+#define AO_ADC_DUMP(p) \
+       printf("tick: %5lu A: %5d B: %5d C: %5d D: %5d drogue: %5d main: %5d batt: %5d pbatt: %5d temp: %5d\n", \
+              (p)->tick, \
+              (p)->adc.sense[0], (p)->adc.sense[1], (p)->adc.sense[2], \
+              (p)->adc.sense[3], (p)->adc.sense[4], (p)->adc.sense[5], \
+              (p)->adc.v_batt, (p)->adc.v_pbatt, (p)->adc.temp)
+
+#define AO_ADC_SENSE_A         0
+#define AO_ADC_SENSE_A_PORT    (&stm_gpioa)
+#define AO_ADC_SENSE_A_PIN     0
+
+#define AO_ADC_SENSE_B         1
+#define AO_ADC_SENSE_B_PORT    (&stm_gpioa)
+#define AO_ADC_SENSE_B_PIN     1
+
+#define AO_ADC_SENSE_C         24
+#define AO_ADC_SENSE_C_PORT    (&stm_gpioe)
+#define AO_ADC_SENSE_C_PIN     9
+
+#define AO_ADC_SENSE_D         25
+#define AO_ADC_SENSE_D_PORT    (&stm_gpioe)
+#define AO_ADC_SENSE_D_PIN     10
+
+#define AO_ADC_SENSE_DROGUE    4
+#define AO_ADC_SENSE_DROGUE_PORT       (&stm_gpioa)
+#define AO_ADC_SENSE_DROGUE_PIN        4
+
+#define AO_ADC_SENSE_MAIN      22
+#define AO_ADC_SENSE_MAIN_PORT (&stm_gpioe)
+#define AO_ADC_SENSE_MAIN_PIN  7
+
+#define AO_ADC_V_BATT          8
+#define AO_ADC_V_BATT_PORT     (&stm_gpiob)
+#define AO_ADC_V_BATT_PIN      0
+
+#define AO_ADC_V_PBATT         9
+#define AO_ADC_V_PBATT_PORT    (&stm_gpiob)
+#define AO_ADC_V_PBATT_PIN     1
+
+#define AO_ADC_TEMP            16
+
+#define AO_ADC_RCC_AHBENR      ((1 << STM_RCC_AHBENR_GPIOAEN) | \
+                                (1 << STM_RCC_AHBENR_GPIOEEN) | \
+                                (1 << STM_RCC_AHBENR_GPIOBEN))
+
+#define AO_NUM_ADC_PIN         (AO_ADC_NUM_SENSE + 2)
+
+#define AO_ADC_PIN0_PORT       AO_ADC_SENSE_A_PORT
+#define AO_ADC_PIN0_PIN                AO_ADC_SENSE_A_PIN
+#define AO_ADC_PIN1_PORT       AO_ADC_SENSE_B_PORT
+#define AO_ADC_PIN1_PIN                AO_ADC_SENSE_B_PIN
+#define AO_ADC_PIN2_PORT       AO_ADC_SENSE_C_PORT
+#define AO_ADC_PIN2_PIN                AO_ADC_SENSE_C_PIN
+#define AO_ADC_PIN3_PORT       AO_ADC_SENSE_D_PORT
+#define AO_ADC_PIN3_PIN                AO_ADC_SENSE_D_PIN
+#define AO_ADC_PIN4_PORT       AO_ADC_SENSE_DROGUE_PORT
+#define AO_ADC_PIN4_PIN                AO_ADC_SENSE_DROGUE_PIN
+#define AO_ADC_PIN5_PORT       AO_ADC_SENSE_MAIN_PORT
+#define AO_ADC_PIN5_PIN                AO_ADC_SENSE_MAIN_PIN
+#define AO_ADC_PIN6_PORT       AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN6_PIN                AO_ADC_V_BATT_PIN
+#define AO_ADC_PIN7_PORT       AO_ADC_V_PBATT_PORT
+#define AO_ADC_PIN7_PIN                AO_ADC_V_PBATT_PIN
+
+#define AO_NUM_ADC             (AO_ADC_NUM_SENSE + 3)
+
+#define AO_ADC_SQ1             AO_ADC_SENSE_A
+#define AO_ADC_SQ2             AO_ADC_SENSE_B
+#define AO_ADC_SQ3             AO_ADC_SENSE_C
+#define AO_ADC_SQ4             AO_ADC_SENSE_D
+#define AO_ADC_SQ5             AO_ADC_SENSE_DROGUE
+#define AO_ADC_SQ6             AO_ADC_SENSE_MAIN
+#define AO_ADC_SQ7             AO_ADC_V_BATT
+#define AO_ADC_SQ8             AO_ADC_V_PBATT
+#define AO_ADC_SQ9             AO_ADC_TEMP
+
+/*
+ * Voltage divider on ADC battery sampler
+ */
+#define AO_BATTERY_DIV_PLUS    56      /* 5.6k */
+#define AO_BATTERY_DIV_MINUS   100     /* 10k */
+
+/*
+ * Voltage divider on ADC igniter samplers
+ */
+#define AO_IGNITE_DIV_PLUS     100     /* 100k */
+#define AO_IGNITE_DIV_MINUS    27      /* 27k */
+
+/*
+ * ADC reference in decivolts
+ */
+#define AO_ADC_REFERENCE_DV    33
+
+/*
+ * Pressure sensor settings
+ */
+#define HAS_MS5607             1
+#define HAS_MS5611             0
+#define AO_MS5607_PRIVATE_PINS 1
+#define AO_MS5607_CS_PORT      (&stm_gpioc)
+#define AO_MS5607_CS_PIN       4
+#define AO_MS5607_CS_MASK      (1 << AO_MS5607_CS)
+#define AO_MS5607_MISO_PORT    (&stm_gpioa)
+#define AO_MS5607_MISO_PIN     6
+#define AO_MS5607_MISO_MASK    (1 << AO_MS5607_MISO)
+#define AO_MS5607_SPI_INDEX    AO_SPI_1_PA5_PA6_PA7
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS          1
+#define AO_M25_SPI_CS_PORT     (&stm_gpiod)
+#define AO_M25_SPI_CS_MASK     (1 << 10)
+#define AO_M25_SPI_BUS         AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Radio (cc1200)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT   5695733
+
+#define AO_FEC_DEBUG           0
+#define AO_CC1200_SPI_CS_PORT  (&stm_gpioc)
+#define AO_CC1200_SPI_CS_PIN   5
+#define AO_CC1200_SPI_BUS      AO_SPI_2_PB13_PB14_PB15
+#define AO_CC1200_SPI          stm_spi2
+
+#define AO_CC1200_INT_PORT             (&stm_gpiob)
+#define AO_CC1200_INT_PIN              11
+
+#define AO_CC1200_INT_GPIO     2
+#define AO_CC1200_INT_GPIO_IOCFG       CC1200_IOCFG2
+
+#define AO_CC1200_MARC_GPIO    3
+#define AO_CC1200_MARC_GPIO_IOCFG      CC1200_IOCFG3
+
+#define HAS_BOOT_RADIO         0
+
+
+/*
+ *
+ * If the board is laying component side up with
+ * the antenna (nose) pointing north
+ *
+ * +along      north   +roll   left up
+ * +across     west    +pitch  nose down
+ * +through    up      +yaw    left turn
+ */
+
+/*
+ * bmi088
+ *
+ *     pin 1 NE corner of chip
+ *
+ *     +along          +Y      +roll   +Y
+ *     +across         -X      +pitch  -X
+ *     +through        +Z      +yaw    +Z
+ */
+
+#define HAS_BMI088             1
+#define AO_BMI088_SPI_BUS      AO_SPI_1_PE13_PE14_PE15
+#define AO_BMI088_ACC_CS_PORT  (&stm_gpioc)
+#define AO_BMI088_ACC_CS_PIN   14
+#define AO_BMI088_GYR_CS_PORT  (&stm_gpioc)
+#define AO_BMI088_GYR_CS_PIN   13
+#define HAS_IMU                        1
+
+#define ao_bmi088_along(m)     ((m)->acc.y)
+#define ao_bmi088_across(m)    (-(m)->acc.x)
+#define ao_bmi088_through(m)   ((m)->acc.z)
+
+#define ao_bmi088_roll(m)      ((m)->gyr.y)
+#define ao_bmi088_pitch(m)     (-(m)->gyr.x)
+#define ao_bmi088_yaw(m)       ((m)->gyr.z)
+
+#define ao_data_along(packet)  ao_bmi088_along(&(packet)->bmi088)
+#define ao_data_across(packet) ao_bmi088_across(&(packet)->bmi088)
+#define ao_data_through(packet)        ao_bmi088_through(&(packet)->bmi088)
+
+#define ao_data_roll(packet)   ao_bmi088_roll(&(packet)->bmi088)
+#define ao_data_pitch(packet)  ao_bmi088_pitch(&(packet)->bmi088)
+#define ao_data_yaw(packet)    ao_bmi088_yaw(&(packet)->bmi088)
+
+/*
+ * MMC5983
+ *
+ *     pin 1 NE corner of chip
+ *
+ *     +along          -Y
+ *     +across         +X
+ *     +through        -Z
+ */
+
+#define HAS_MMC5983            1
+#define AO_MMC5983_INT_PORT    (&stm_gpiod)
+#define AO_MMC5983_INT_PIN     5
+#define AO_MMC5983_SPI_CLK_PORT        (&stm_gpiod)
+#define AO_MMC5983_SPI_CLK_PIN 1
+#define AO_MMC5983_SPI_MISO_PORT       (&stm_gpiod)
+#define AO_MMC5983_SPI_MISO_PIN        3
+#define AO_MMC5983_SPI_MOSI_PORT       (&stm_gpiod)
+#define AO_MMC5983_SPI_MOSI_PIN        4
+#define AO_MMC5983_SPI_INDEX   (AO_SPI_2_PD1_PD3_PD4 | AO_SPI_MODE_3)
+#define AO_MMC5983_SPI_CS_PORT (&stm_gpioa)
+#define AO_MMC5983_SPI_CS_PIN  15
+
+#define ao_mmc5983_along(m)            (-(m)->y)
+#define ao_mmc5983_across(m)           ((m)->x)
+#define ao_mmc5983_through(m)          (-(m)->z)
+
+#define ao_data_mag_along(packet)      ao_mmc5983_along(&(packet)->mmc5983)
+#define ao_data_mag_across(packet)     ao_mmc5983_across(&(packet)->mmc5983)
+#define ao_data_mag_through(packet)    ao_mmc5983_through(&(packet)->mmc5983)
+
+/*
+ * ADXL375
+ *
+ * pin 1 NW corner of chip
+ *
+ *     +along          +X
+ *     +across         +Y
+ *     +through        +Z
+ */
+
+#define HAS_ADXL375            1
+#define AO_ADXL375_SPI_INDEX   (AO_SPI_1_PB3_PB4_PB5 | AO_SPI_MODE_3)
+#define AO_ADXL375_CS_PORT     (&stm_gpioc)
+#define AO_ADXL375_CS_PIN      0
+
+#define AO_ADXL375_AXIS                x
+#define AO_ADXL375_INVERT      1
+
+#define NUM_CMDS               16
+
+/*
+ * Companion
+ */
+
+#define AO_COMPANION_CS_PORT   (&stm_gpiob)
+#define AO_COMPANION_CS_PIN_0  (6)
+#define AO_COMPANION_CS_PIN    AO_COMPANION_CS_PIN_0
+#define AO_COMPANION_CS_PIN_1  (7)
+#define AO_COMPANION_SPI_BUS   AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Monitor
+ */
+
+#define HAS_MONITOR            0
+#define LEGACY_MONITOR         0
+#define HAS_MONITOR_PUT                1
+#define AO_MONITOR_LED         0
+#define HAS_RSSI               0
+
+/*
+ * Profiling Viterbi decoding
+ */
+
+#ifndef AO_PROFILE
+#define AO_PROFILE             0
+#endif
+
+/*
+ * PWM output
+ */
+
+#define NUM_PWM                        4
+#define PWM_MAX                        20000
+#define AO_PWM_TIMER           stm_tim4
+#define AO_PWM_TIMER_ENABLE    STM_RCC_APB1ENR_TIM4EN
+#define AO_PWM_TIMER_SCALE     32
+
+#define AO_PWM_0_GPIO          (&stm_gpiod)
+#define AO_PWM_0_PIN           12
+
+#define AO_PWM_1_GPIO          (&stm_gpiod)
+#define AO_PWM_1_PIN           13
+
+#define AO_PWM_2_GPIO          (&stm_gpiod)
+#define AO_PWM_2_PIN           15
+
+#define AO_PWM_3_GPIO          (&stm_gpiod)
+#define AO_PWM_3_PIN           14
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telemega-v6.0/ao_telemega.c b/src/telemega-v6.0/ao_telemega.c
new file mode 100644 (file)
index 0000000..91526a8
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright © 2021 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_bmi088.h>
+#include <ao_mmc5983.h>
+#include <ao_adxl375.h>
+#include <ao_log.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_profile.h>
+#include <ao_eeprom.h>
+#include <ao_i2c_bit.h>
+#if HAS_SAMPLE_PROFILE
+#include <ao_sample_profile.h>
+#endif
+#include <ao_pyro.h>
+#if HAS_STACK_GUARD
+#include <ao_mpu.h>
+#endif
+#include <ao_pwm.h>
+
+int
+main(void)
+{
+       ao_clock_init();
+
+#if HAS_STACK_GUARD
+       ao_mpu_init();
+#endif
+
+       ao_task_init();
+       ao_serial_init();
+       ao_led_init();
+       ao_led_on(LEDS_AVAILABLE);
+       ao_timer_init();
+
+       ao_spi_init();
+#ifdef MMC5983_I2C
+       ao_i2c_bit_init();
+#endif
+       ao_dma_init();
+       ao_exti_init();
+
+       ao_adc_init();
+#if HAS_BEEP
+       ao_beep_init();
+#endif
+       ao_cmd_init();
+
+       ao_ms5607_init();
+       ao_bmi088_init();
+       ao_mmc5983_init();
+       ao_adxl375_init();
+
+       ao_eeprom_init();
+       ao_storage_init();
+
+       ao_flight_init();
+       ao_log_init();
+       ao_report_init();
+
+       ao_usb_init();
+       ao_gps_init();
+       ao_gps_report_mega_init();
+       ao_telemetry_init();
+       ao_radio_init();
+       ao_packet_slave_init(false);
+       ao_igniter_init();
+       ao_companion_init();
+       ao_pyro_init();
+
+       ao_config_init();
+#if AO_PROFILE
+       ao_profile_init();
+#endif
+#if HAS_SAMPLE_PROFILE
+       ao_sample_profile_init();
+#endif
+
+       ao_pwm_init();
+
+       ao_led_off(LEDS_AVAILABLE);
+
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/telemega-v6.0/flash-loader/Makefile b/src/telemega-v6.0/flash-loader/Makefile
new file mode 100644 (file)
index 0000000..19fe40e
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telemega-v6.0
+include $(TOPDIR)/stm/Makefile-flash.defs
diff --git a/src/telemega-v6.0/flash-loader/ao_pins.h b/src/telemega-v6.0/flash-loader/ao_pins.h
new file mode 100644 (file)
index 0000000..46ba162
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2021 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* External crystal at 16MHz */
+#define AO_HSE         16000000
+
+#include <ao_flash_stm_pins.h>
+
+/* Companion port cs_companion0 PB6 */
+
+#define AO_BOOT_PIN                    1
+#define AO_BOOT_APPLICATION_GPIO       stm_gpiob
+#define AO_BOOT_APPLICATION_PIN                6
+#define AO_BOOT_APPLICATION_VALUE      1
+#define AO_BOOT_APPLICATION_MODE       AO_EXTI_MODE_PULL_UP
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telemetrum-v4.0/Makefile b/src/telemetrum-v4.0/Makefile
new file mode 100644 (file)
index 0000000..5191640
--- /dev/null
@@ -0,0 +1,122 @@
+#
+# 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_ms5607.h \
+        ao_adxl375.h \
+        ao_cc1200_CC1200.h \
+        ao_task.h \
+        ao_whiten.h \
+       samd21.h \
+       Makefile
+
+# SAMD21G17D
+
+SAMD21_ROM=128
+SAMD21_RAM=16
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+#SAMPLE_PROFILE=ao_sample_profile.c \
+#       ao_sample_profile_timer.c
+#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1
+
+#STACK_GUARD=ao_mpu_stm.c
+#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
+
+ALTOS_SRC = \
+       ao_boot_chain.c \
+        ao_interrupt.c \
+        ao_product.c \
+        ao_romconfig.c \
+        ao_cmd.c \
+        ao_config.c \
+        ao_task.c \
+        ao_led.c \
+        ao_stdio.c \
+        ao_panic.c \
+        ao_timer.c \
+        ao_mutex.c \
+        ao_serial_samd21.c \
+        ao_gps_ublox.c \
+        ao_gps_show.c \
+        ao_gps_report_metrum.c \
+        ao_ignite.c \
+        ao_freq.c \
+        ao_dma_samd21.c \
+        ao_spi_samd21.c \
+        ao_cc1200.c \
+        ao_data.c \
+        ao_ms5607.c \
+        ao_adxl375.c \
+        ao_adc_samd21.c \
+        ao_beep_samd21.c \
+        ao_storage.c \
+        ao_m25.c \
+       ao_usb_samd21.c \
+       ao_exti_samd21.c \
+        ao_report.c \
+        ao_convert_pa.c \
+        ao_convert_volt.c \
+        ao_log.c \
+        ao_log_metrum.c \
+        ao_sample.c \
+        ao_kalman.c \
+        ao_flight.c \
+        ao_telemetry.c \
+        ao_packet_slave.c \
+        ao_packet.c \
+        ao_companion.c \
+        ao_aprs.c \
+        $(PROFILE) \
+        $(SAMPLE_PROFILE) \
+        $(STACK_GUARD)
+
+PRODUCT=TeleMetrum-v4.0
+PRODUCT_DEF=-DTELEMETRUM_V_4_0
+IDPRODUCT=0x000b
+
+CFLAGS = $(PRODUCT_DEF) $(SAMD21_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF)
+
+PROGNAME=telemetrum-v4.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_telemetrum.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ)
+       $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+load: $(PROG)
+       stm-load $(PROG)
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/telemetrum-v4.0/ao_pins.h b/src/telemetrum-v4.0/ao_pins.h
new file mode 100644 (file)
index 0000000..bc05761
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_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 HAS_SERIAL_1           1
+#define USE_SERIAL_1_STDIN     0
+
+#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX       (512 * 1024)
+#define AO_CONFIG_MAX_SIZE                     1024
+#define LOG_ERASE_MARK                         0x55
+#define LOG_MAX_ERASE                          128
+#define AO_LOG_FORMAT                          AO_LOG_FORMAT_TELEMETRUM
+
+#define HAS_EEPROM             1
+#define USE_INTERNAL_FLASH     0
+#define USE_EEPROM_CONFIG      0
+#define USE_STORAGE_CONFIG     1
+#define HAS_USB                        1
+#define USE_USB_STDIO  1
+#define HAS_BATTERY_REPORT     1
+#define BEEPER_CHANNEL         4
+#define BEEPER_TIMER           3
+#define BEEPER_PORT            (&samd21_port_a)
+#define BEEPER_PIN             16
+#define HAS_RADIO              1
+#define HAS_RADIO_10MW         1
+#define HAS_TELEMETRY          1
+#define HAS_APRS               1
+#define HAS_COMPANION          1
+
+#define HAS_SPI_0              1
+#define HAS_SPI_3              1
+#define HAS_SPI_5              1
+
+#define PACKET_HAS_SLAVE       1
+#define PACKET_HAS_MASTER      0
+
+#define LOW_LEVEL_DEBUG                0
+
+#define HAS_LED                        1
+#define LED_0_PORT             (&samd21_port_b)
+#define LED_0_PIN              10
+#define LED_1_PORT             (&samd21_port_b)
+#define LED_1_PIN              11
+#define AO_LED_RED             (1 << 0)
+#define AO_LED_GREEN           (1 << 1)
+
+#define HAS_GPS                        1
+#define HAS_FLIGHT             1
+#define HAS_ADC                        1
+#define HAS_ADC_TEMP           1
+#define HAS_LOG                        1
+
+/*
+ * Beeper
+ */
+
+#define HAS_BEEP               1
+/* Beep on PA16 function E TCC2.0 */
+
+#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
+
+/*
+ * Igniter
+ */
+
+#define HAS_IGNITE             1
+#define HAS_IGNITE_REPORT      1
+
+#define AO_SENSE_DROGUE(p)     ((p)->adc.sense_a)
+#define AO_SENSE_MAIN(p)       ((p)->adc.sense_m)
+#define AO_IGNITER_CLOSED      400
+#define AO_IGNITER_OPEN                60
+
+/* Drogue */
+#define AO_IGNITER_DROGUE_PORT (&samd21_port_a)
+#define AO_IGNITER_DROGUE_PIN  19
+
+/* Main */
+#define AO_IGNITER_MAIN_PORT   (&samd21_port_a)
+#define AO_IGNITER_MAIN_PIN    18
+
+/*
+ * ADC
+ */
+#define AO_DATA_RING           32
+#define AO_ADC_NUM_SENSE       2
+
+struct ao_adc {
+       int16_t                 sense_a;
+       int16_t                 sense_m;
+       int16_t                 v_batt;
+       int16_t                 temp;
+};
+
+#define AO_ADC_DUMP(p) \
+       printf("tick: %5lu drogue: %5d main: %5d batt: %5d\n", \
+              (p)->tick, \
+              (p)->adc.sense_a, (p)->adc.sense_m, \
+              (p)->adc.v_batt);
+
+#define AO_ADC_SENSE_DROGUE            18
+#define AO_ADC_SENSE_DROGUE_PORT       (&samd21_port_a)
+#define AO_ADC_SENSE_DROGUE_PIN                10
+
+#define AO_ADC_SENSE_MAIN              19
+#define AO_ADC_SENSE_MAIN_PORT         (&samd21_port_a)
+#define AO_ADC_SENSE_MAIN_PIN          11
+
+#define AO_ADC_V_BATT                  17
+#define AO_ADC_V_BATT_PORT             (&samd21_port_a)
+#define AO_ADC_V_BATT_PIN              9
+
+#define AO_ADC_TEMP                    SAMD21_ADC_INPUTCTRL_MUXPOS_TEMP
+
+#define AO_NUM_ADC_PIN                 3
+
+#define AO_ADC_PIN0_PORT       AO_ADC_SENSE_DROGUE_PORT
+#define AO_ADC_PIN0_PIN                AO_ADC_SENSE_DROGUE_PIN
+#define AO_ADC_PIN1_PORT       AO_ADC_SENSE_MAIN_PORT
+#define AO_ADC_PIN1_PIN                AO_ADC_SENSE_MAIN_PIN
+#define AO_ADC_PIN2_PORT       AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN2_PIN                AO_ADC_V_BATT_PIN
+
+#define AO_NUM_ADC             (AO_NUM_ADC_PIN + 1)
+
+#define AO_ADC_SQ0             AO_ADC_SENSE_DROGUE
+#define AO_ADC_SQ1             AO_ADC_SENSE_MAIN
+#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    56      /* 5.6k */
+#define AO_BATTERY_DIV_MINUS   100     /* 10k */
+
+/*
+ * Voltage divider on ADC igniter samplers
+ */
+#define AO_IGNITE_DIV_PLUS     100     /* 100k */
+#define AO_IGNITE_DIV_MINUS    27      /* 27k */
+
+/*
+ * ADC reference in decivolts
+ */
+#define AO_ADC_REFERENCE_DV    33
+
+/*
+ * GPS
+ */
+
+#define AO_SERIAL_SPEED_UBLOX  AO_SERIAL_SPEED_9600
+
+#define HAS_SERIAL_1           1
+#define USE_SERIAL_1_STDIN     0
+#define SERIAL_1_PA00_PA01     1
+
+#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_samd21_usart1.rx_fifo)
+
+/*
+ * Pressure sensor settings
+ */
+#define HAS_MS5607             1
+#define HAS_MS5611             0
+#define AO_MS5607_PRIVATE_PINS 0
+#define AO_MS5607_CS_PORT      (&samd21_port_a)
+#define AO_MS5607_CS_PIN       21
+#define AO_MS5607_MISO_PORT    (&samd21_port_a)
+#define AO_MS5607_MISO_PIN     20
+#define AO_MS5607_SPI_INDEX    AO_SPI_3_PA22_PA23_PA20
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS          1
+#define AO_M25_SPI_CS_PORT     (&samd21_port_a)
+#define AO_M25_SPI_CS_MASK     (1 << 27)
+#define AO_M25_SPI_BUS         AO_SPI_5_PB22_PB23_PB03
+
+
+/*
+ * Radio (cc1200)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT   5695733
+
+#define AO_CC1200_SPI_CS_PORT  (&samd21_port_a)
+#define AO_CC1200_SPI_CS_PIN   7
+#define AO_CC1200_SPI_BUS      AO_SPI_5_PB22_PB23_PB03
+
+#define AO_CC1200_INT_PORT             (&samd21_port_b)
+#define AO_CC1200_INT_PIN              (8)
+#define AO_CC1200_MCU_WAKEUP_PORT      (&samd21_port_b)
+#define AO_CC1200_MCU_WAKEUP_PIN       (9)
+
+#define AO_CC1200_INT_GPIO     2
+#define AO_CC1200_INT_GPIO_IOCFG       CC1200_IOCFG2
+
+#define AO_CC1200_MARC_GPIO    3
+#define AO_CC1200_MARC_GPIO_IOCFG      CC1200_IOCFG3
+
+#define HAS_BOOT_RADIO         0
+
+#define HAS_HIGHG_ACCEL                1
+
+/* ADXL375 */
+
+#define HAS_ADXL375            1
+#define AO_ADXL375_CS_PORT     (&samd21_port_a)
+#define AO_ADXL375_CS_PIN      8
+#define AO_ADXL375_SPI_INDEX   (AO_SPI_0_PA04_PA05_PA06 | AO_SPI_MODE_3)
+
+#define AO_ADXL375_AXIS                x
+#define AO_ADXL375_INVERT      1
+
+#define NUM_CMDS               16
+
+/*
+ * Companion
+ */
+
+#define AO_COMPANION_CS_PORT   (&samd21_port_a)
+#define AO_COMPANION_CS_PIN    (13)
+#define AO_COMPANION_SPI_BUS   AO_SPI_5_PB22_PB23_PB03
+
+/*
+ * Monitor
+ */
+
+#define HAS_MONITOR            0
+#define LEGACY_MONITOR         0
+#define HAS_MONITOR_PUT                1
+#define AO_MONITOR_LED         0
+#define HAS_RSSI               0
+
+/*
+ * Profiling Viterbi decoding
+ */
+
+#ifndef AO_PROFILE
+#define AO_PROFILE             0
+#endif
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telemetrum-v4.0/ao_telemetrum.c b/src/telemetrum-v4.0/ao_telemetrum.c
new file mode 100644 (file)
index 0000000..db0fc6a
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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_adxl375.h>
+#include <ao_log.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_eeprom.h>
+#if HAS_SAMPLE_PROFILE
+#include <ao_profile.h>
+#include <ao_sample_profile.h>
+#endif
+#if HAS_STACK_GUARD
+#include <ao_mpu.h>
+#endif
+#include <ao_dma_samd21.h>
+
+int
+main(void)
+{
+       ao_clock_init();
+
+#if HAS_STACK_GUARD
+       ao_mpu_init();
+#endif
+
+       ao_task_init();
+       ao_serial_init();
+       ao_led_init();
+       ao_led_on(LEDS_AVAILABLE);
+       ao_timer_init();
+
+       ao_spi_init();
+       ao_dma_init();
+       ao_exti_init();
+
+       ao_adc_init();
+       ao_beep_init();
+       ao_cmd_init();
+
+       ao_ms5607_init();
+       ao_adxl375_init();
+
+       ao_storage_init();
+
+       ao_flight_init();
+       ao_log_init();
+       ao_report_init();
+
+       ao_usb_init();
+       ao_gps_init();
+       ao_gps_report_metrum_init();
+       ao_telemetry_init();
+       ao_radio_init();
+       ao_packet_slave_init(false);
+       ao_igniter_init();
+       ao_companion_init();
+
+       ao_config_init();
+#if AO_PROFILE
+       ao_profile_init();
+#endif
+#if HAS_SAMPLE_PROFILE
+       ao_sample_profile_init();
+#endif
+       ao_led_off(LEDS_AVAILABLE);
+
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/telemetrum-v4.0/flash-loader/Makefile b/src/telemetrum-v4.0/flash-loader/Makefile
new file mode 100644 (file)
index 0000000..5fabbc0
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telemetrum-v4.0
+include $(TOPDIR)/samd21/Makefile-flash.defs
diff --git a/src/telemetrum-v4.0/flash-loader/ao_pins.h b/src/telemetrum-v4.0/flash-loader/ao_pins.h
new file mode 100644 (file)
index 0000000..5653c24
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2022 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_
+
+#include <ao_flash_samd21_pins.h>
+
+/* cs_comp_0 (companion port pin 6) to gnd for boot loader mode */
+
+#define AO_BOOT_PIN                    1
+#define AO_BOOT_APPLICATION_GPIO       (samd21_port_a)
+#define AO_BOOT_APPLICATION_PIN                13
+#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_ */
index 5617b8e435e09818b53b7e94932819675a1d18e7..fa74fc19b56debecf3b59dbab98d3389212b00a5 100644 (file)
@@ -40,6 +40,7 @@ public class TeleGPSConfigUI
        JLabel                  radio_frequency_label;
        JLabel                  radio_enable_label;
        JLabel                  radio_10mw_label;
+       JLabel                  report_feet_label;
        JLabel                  rate_label;
        JLabel                  aprs_interval_label;
        JLabel                  aprs_ssid_label;
@@ -60,6 +61,7 @@ public class TeleGPSConfigUI
        JLabel                  radio_calibration_value;
        JRadioButton            radio_enable_value;
        JRadioButton            radio_10mw_value;
+       JComboBox<String>       report_feet_value;
        AltosUIRateList         rate_value;
        JComboBox<String>       aprs_interval_value;
        JComboBox<Integer>      aprs_ssid_value;
@@ -113,6 +115,11 @@ public class TeleGPSConfigUI
                "10"
        };
 
+       static String[]         report_feet_values = {
+               "Meters",
+               "Feet",
+       };
+
        /* A window listener to catch closing events and tell the config code */
        class ConfigListener extends WindowAdapter {
                TeleGPSConfigUI ui;
@@ -161,6 +168,38 @@ public class TeleGPSConfigUI
                        radio_10mw_value.setToolTipText("Older firmware could not limit radio power");
        }
 
+       void set_report_feet_tool_tip() {
+               if (report_feet_value.isVisible())
+                       report_feet_value.setToolTipText("Units used after landing to beep max height");
+               else
+                       report_feet_value.setToolTipText("Older firmware always beeps max height in meters");
+       }
+
+       public void set_report_feet(int new_report_feet) {
+               if (new_report_feet != AltosLib.MISSING) {
+                       if (new_report_feet >= report_feet_values.length)
+                               new_report_feet = 0;
+                       if (new_report_feet < 0) {
+                               report_feet_value.setEnabled(false);
+                               new_report_feet = 0;
+                       } else {
+                               report_feet_value.setEnabled(true);
+                       }
+                       report_feet_value.setSelectedIndex(new_report_feet);
+               }
+               report_feet_value.setVisible(new_report_feet != AltosLib.MISSING);
+               report_feet_label.setVisible(new_report_feet != AltosLib.MISSING);
+
+               set_report_feet_tool_tip();
+       }
+
+       public int report_feet() {
+               if (report_feet_value.isVisible())
+                       return report_feet_value.getSelectedIndex();
+               else
+                       return AltosLib.MISSING;
+       }
+
        void set_rate_tool_tip() {
                if (rate_value.isVisible())
                        rate_value.setToolTipText("Select telemetry baud rate");