+commit 8e42773e7834638464b46495123dbdc2356032bf
+Merge: a6c32739 7fe1408a
+Author: Bdale Garbee <bdale@gag.com>
+Date: Thu Jan 19 22:55:56 2023 -0700
+
+ Merge branch 'master' into branch-1.9
+
+commit 7fe1408af6fd65c13cebf33d7bb7bf8517d8ce1e
+Merge: 234b275d 5f007934
+Author: Bdale Garbee <bdale@gag.com>
+Date: Thu Jan 19 22:51:27 2023 -0700
+
+ Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 5f007934c12863b5df1adc8d286b20b3488b5a6f
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Jan 19 17:28:36 2023 -0800
+
+ Version 1.9.13
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 97e6f06783006f59dd596d4f3d741408a1e4e5b1
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Jan 19 17:27:18 2023 -0800
+
+ doc: Update copyright year to 2023
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1803c15b9d9110df9e54374b2576681835d6cd87
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Jan 19 17:27:02 2023 -0800
+
+ doc: Add release notes for 1.9.13
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 96181ff1e0d376d7a2c9519abbf4f346d8fd47ea
+Author: Bdale Garbee <bdale@gag.com>
+Date: Thu Jan 19 22:41:24 2023 -0700
+
+ doc: update EasyMotor behavior description for firmware 1.9.13 and later
+
+commit 234b275db125304829d1859713388775fd3791db
+Author: Bdale Garbee <bdale@gag.com>
+Date: Thu Jan 19 22:41:24 2023 -0700
+
+ doc: update EasyMotor behavior description for firmware 1.9.13 and later
+
+commit 22f9fb47d97ba13f6b30fab25bce552bccc91997
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Jan 19 17:39:42 2023 -0800
+
+ Disable TM v4.0 firmware for 1.9.13
+
+ This will be in 1.9.14
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3021764629a438d1d1b0323a09d6fb2350ce1bfb
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Jan 19 17:28:36 2023 -0800
+
+ Version 1.9.13
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2689b30cdccba18d46505774aafc8e6119948191
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Jan 19 17:27:18 2023 -0800
+
+ doc: Update copyright year to 2023
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 399c85245542d95f05063fdd34939dc746f1ea2d
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Jan 19 17:27:02 2023 -0800
+
+ doc: Add release notes for 1.9.13
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 691869aa6c542999a4e915c23b6696451dd42f65
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Jan 19 17:20:44 2023 -0800
+
+ altos: Add makefiles for telemega v6
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e4ce0ce186b68497cfb14a400410fdd38aa93abc
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Jan 19 14:04:50 2023 -0800
+
+ altos/easymotor: Move pressure conversion code to ao_motor_flight.c
+
+ This inline function uses a pile of constants which aren't defined in
+ ao_pins.h, so move it to ao_motor_flight.c where it is used
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 28e4cb24e0f0ee5abf66c5a0466edfd0e31f4df0
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Jan 19 13:20:56 2023 -0800
+
+ easymotor-v3: Fix pressure to adc conversion to use reference voltage
+
+ Wasn't taking reference voltage into account, which meant the computed values
+ were 3.3 times too high.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7b40813fdafce4ebd635aceb4c32355339a4d98d
+Merge: 026ab589 4c61c113
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Jan 19 12:21:32 2023 -0800
+
+ Merge branch 'easymotor-flight'
+
+commit 026ab589d0ade3dd59d26888fffd8699ef534671
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Jan 19 12:18:46 2023 -0800
+
+ altos/telelco: Add some debug aids for TeleLCO event stuff
+
+ Use (debug & 2) to select just event debugging.
+ Add 'pretend' mode to cycle through all possible pad boxes.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ee49d3c202e1fee14328006e2beb2e774313de2e
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Dec 27 21:33:14 2022 -0800
+
+ altoslib: Add TeleMega v6 support
+
+ New sensor (bmi088) support, plus other changes as needed. Also found
+ some places missing TeleMega-v5 support?
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 96444c1e0e6a1c179d9695b454012d0b16d0a76c
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Dec 27 21:32:39 2022 -0800
+
+ ao-tools: Update ao-eeprom for TeleMega v6
+
+ Add new log file format; no other changes needed here.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 95d39febfa9b6c6f2696fb1b47cf72aa808ec7c9
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Dec 27 21:31:38 2022 -0800
+
+ ao-tools: Fix buffer overflow in lib/ao-hex.c
+
+ ao_hex_file_create allocated a buffer too short when writing EOF.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 08f13d3301bfcf9a5b9b566df4ffd4ed33f236d0
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Dec 27 21:30:13 2022 -0800
+
+ altos: Split AO_LOG_NORMALIZED support out in telemetry code
+
+ This avoids needing per-driver changes to the telemetry code by using
+ the normalized data gathering techniques.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0d16160ce2207fd248c2d93a4f7a77a05176204d
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Dec 27 21:29:12 2022 -0800
+
+ drivers: Add AO_LOG_NORMALIZED support to BMI088 command
+
+ Make this driver work in monitor idle mode
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ad4679dd0bd2d759f708a6ab6d0425eb1cbde7c1
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Dec 27 21:01:06 2022 -0800
+
+ src/drivers: Ignore printf warnings in ao_aprs.c
+
+ This code uses sprintf to generate the tnc buffer, but the compiler doesn't
+ really understand the value limits.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0c34739489b23e729cd7d35c5ec74416fb51164a
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Dec 27 20:24:51 2022 -0800
+
+ doc: Update docs for asciidoctor-pdf version 2.x
+
+ Some of the theming stuff changed
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fc042fa0a77d808fa7670e213e7e0c273200a043
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Dec 17 18:01:01 2022 -0800
+
+ altos: Add TeleMega v6.0
+
+ Like TeleMega v5.0, but replaces MPU6000 with BMI088
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9d515a031d388bec41583f0335e97ecd78c75864
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Aug 27 17:06:58 2022 -0700
+
+ altos: Add BMI088 test framework using Nucleo32 board for stm32f042
+
+ This allows a BMI088 chip to be connected to a nucleo-32 board
+ with an stm32f042 chip to be tested.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bc0adb3723e9d383c8a379850c4cb0650003772e
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Aug 27 17:05:51 2022 -0700
+
+ drivers: Add BMI088 driver
+
+ Supports the Bosch BMI-088 3-d accel/gyro device.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 560767497057c08a6fc3bc24aaba23a8d6192259
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Dec 5 19:26:42 2022 -0800
+
+ altos: Add timeout in MS5607 get_sample code
+
+ MISO is supposed to rise when the sample value is ready, but sometimes
+ we miss the interrupt which should be generated. I spent a day
+ attempting to fix this, but was unable to make it 100%. Instead, add a
+ 10ms timeout, which is longer than the sample time (8.2ms), and then
+ read MISO directly. If the interrupt fires, we'll read a bit sooner.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4af1fba120644f38e91bae283c9ff2ef32c2e49c
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Dec 5 19:51:02 2022 -0800
+
+ altos/stmf0: Add ao_spi_put_pins for MS5067 driver
+
+ TeleMini needs this on stmf0
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2ea3f551b3c4649320efe2fd999f01012bca03bd
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Dec 5 19:29:28 2022 -0800
+
+ altos/stm: Add ao_spi_put_pins for MS5607 driver
+
+ This function re-configures the GPIO mux to disconnect the SPI block
+ from the pins driving the MS5607.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4c61c11322b57ed1910bba33bd2ce11a368e6583
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Dec 4 16:33:02 2022 -0800
+
+ easymotor-v3.0: Clean up build bits
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1a467bf13485649419e1205ee788ef0d58b42d01
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Dec 4 16:32:05 2022 -0800
+
+ easymotor-v3.0: Use motor pressure to trigger data logging
+
+ Use this instead of acceleration so it can be used on the ground as
+ well as for staged flights.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit da253f1ee9510fba93ddf5c1895570eb5081af46
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Dec 4 14:17:58 2022 -0800
+
+ altos: Add timeout for MS5607 ADC conversion
+
+ The MS5607 should signal conversion done by pulling MISO low, but in
+ case it doesn't, add a 10ms timeout to wake up after the conversion
+ should be done.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c2eb61af64b8f38d88fff885f0fccdc0100ab233
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 26 09:57:33 2022 -0800
+
+ altoslib: Add notes about updating ao-eeprom for new product
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c1708f3fa4ff412da8817ba0fa58d05fe7ef44f5
+Merge: 414ad269 999cc72a
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 26 09:52:37 2022 -0800
+
+ Merge branch 'master'
+
+commit 414ad2696bbe2a2f245ae7bb2ec8f57bc8cc0097
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 26 09:48:19 2022 -0800
+
+ ao-tools/ao-eeprom: Add support for TeleMega v5.0
+
+ Same as TeleMega v4.0 (ao-eeprom doesn't do much with imu data)
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 999cc72af08ebf9ce0fd44ed00b42a2cd1a53594
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 20 15:57:44 2022 -0800
+
+ Add TeleMetrum v4.0 to release files
+
+ Releasing
+ altosui/Makefile
+ altosui/altos-windows.nsi.in
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6cb7457b87913d5f18b6b87d401b2782d8f665a4
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 20 15:56:06 2022 -0800
+
+ altoslib: Handle TeleMetrum v4.0 in idle mode
+
+ Handle config data and idle fetch stuff. Note that TM v4.0 differs
+ from older versions a bit as it stores config data in the SPI flash
+ chip, not in the SoC.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f80839cec9fef10e182901e08dec81a41c28f34b
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 20 15:31:27 2022 -0800
+
+ altos: Enable telemetrum telemetry for telemetrum v4.0
+
+ This configuration could be done better ...
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a616233dcdb3bcc1ac2b16436f3c6a0f3c746a63
+Author: Bdale Garbee <bdale@gag.com>
+Date: Fri Nov 18 13:06:12 2022 -0700
+
+ doc: add a link to a reference on ARES/RACES use of PowerPole connectors
+
+commit abcc8ca13005155b11d2316cf88be8cfa0294f44
+Author: Bdale Garbee <bdale@gag.com>
+Date: Fri Nov 18 12:51:08 2022 -0700
+
+ doc: add a warning to TeleLaunch docs about siren volume levels
+
+commit 4ff9f3271f9d20603626c18ee16b20e50c579ddf
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 23:37:29 2022 -0800
+
+ altos: Make sure APRS packets are reasonably separated
+
+ Don't let them come too close together.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b4960336c0ae960bee40fdd337e58fcc8846ac87
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 23:35:40 2022 -0800
+
+ altos/ms5607: Fixup ms5607 changes for other chips
+
+ A couple of chips don't bother providing duplex, so allow for that.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6c3f98370a7244a053f8e4858b8f136e2fd659b2
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 23:34:34 2022 -0800
+
+ altos: Add ao_gps_utc_tick
+
+ This indicates which tick is associated with the UTC time found
+ in the current ao gps data
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ce20ccccdf9464877a73f6ff2a5f9ce91f88fc27
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 23:33:29 2022 -0800
+
+ attiny, stm32l0: Note that these chips don't support spi duplex
+
+ This makes the ms5607 driver use send/recv instead.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1bf40aa4c9ea9844046b7b84342225274622d7c2
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 22:48:28 2022 -0800
+
+ samd21: Save interrupt pin status before enabling interrupts
+
+ Make sure we capture the correct external interrupt pin state before
+ anything changes.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8c0a7dfa4ba4b46dabce0ac7daecf0edd5fb6b62
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 22:47:00 2022 -0800
+
+ altos: Avoid modulus in task queue on parts without idiv
+
+ Cortex-M0 parts don't have a hardware divide, so doing a modulus
+ to compute task hash indexes is super slow. Avoid that by making the sleep
+ queue a power of two.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bf51520898fb30b289b2e03b31a1719c172cf422
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 22:38:12 2022 -0800
+
+ altos/cc1200: Simplify interrupt handling
+
+ Use PKT_SYNC_RXTX for both packet send/recv signaling. This avoids
+ needing to flip the interrupt sense around between tx and rx, allowing
+ us to leave interrupts always enabled which avoids adventures on
+ samd21 with interrupt configuration.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9ed81c072f0c4c2044a284176d66be00a1439a40
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 20:44:38 2022 -0800
+
+ telemetrum-v4.0: Correct beeper configuration
+
+ Beeper is on PA16, which is TCC2/0 with config E
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a69c3009a0583d04d1f52a88f4b0135b1944ee35
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 19:39:18 2022 -0800
+
+ telemetrum-v4.0: MS5607 doesn't share SPI controller
+
+ The PRIVATE_PINS value is a bit misleading.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c450b5cd80534d067a760e5f0768f39a20604761
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 19:38:56 2022 -0800
+
+ fixup for exti restore
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c82b214e34763e0206cd676cae2d82e735ef2576
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 19:38:09 2022 -0800
+
+ samd21: AO_SPI_0_PA04_PA05_PA06 is FUNC_D
+
+ Not FUNC_C
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 479bf2d88544f7f848ae42f77b0fbb74ca61ca27
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 19:37:23 2022 -0800
+
+ samd21: Restore pin cfg when exti is disabled
+
+ Instead of clearing the pmux, set it back to the previous value as is
+ needed by ms5607.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1cdcaea940864bbe531b32f228bc169b50f4d3a9
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 19:36:52 2022 -0800
+
+ samd21: Limit SPI baud value to 1
+
+ Keep speeds slow enough
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4cb051d8ec1cdd7fd9cb87129c7425feb21f284e
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 19:36:10 2022 -0800
+
+ samd21: PA20 maps to SERCOM3.2 not SERCOM3.3
+
+ Flip the DIPO config around for AO_SPI_3_PA22_PA23_PA20
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5c182ab460fef614e2b987c808d4b4e2c4240a4f
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 12 19:35:26 2022 -0800
+
+ altos/ms5607: Use duplex for fetching data
+
+ Much more efficient than send/recv
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 997d68e13c32118c665fa273c515726c5ab2ce6b
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Nov 10 11:47:39 2022 -0800
+
+ telegps: Copy altosui 'beep feet' bits to telegps
+
+ Yeah, this is cut&paste coding. Sorry.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit af241f03c5ec286f536c367b8cd27e9083af792b
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Nov 10 11:40:29 2022 -0800
+
+ telemetrum-v4.0: Set USE_SERIAL_1_STDIN to 0
+
+ This makes sure the serial driver knows how to manage this port
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 53a9d3b3c2d9fb04d8f5fba42ce19cd13dc65f28
+Merge: 31f32f20 543b6d08
+Author: Bdale Garbee <bdale@gag.com>
+Date: Thu Nov 10 12:34:49 2022 -0700
+
+ Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 31f32f200c3f64b42617638eb2fc390857d2b2e6
+Author: Bdale Garbee <bdale@gag.com>
+Date: Thu Nov 10 12:34:12 2022 -0700
+
+ ao-tools: return ao-flash-lpc operaton to "full speed"
+
+commit abd437a372932dcc1aafe939e65919a50c203af0
+Author: Keith Packard <keithp@keithp.com>
+Date: Wed Nov 9 16:29:56 2022 -0800
+
+ metro-m0: Add GPS support
+
+ Plug a ublox GPS module into D4(tx)/D3(rx)
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4bd22c0cf3fc0358cff263e63ad2cc788be2bb14
+Author: Keith Packard <keithp@keithp.com>
+Date: Wed Nov 9 16:29:13 2022 -0800
+
+ samd21: Leave serial RX interrupts enabled
+
+ Just discard characters in the ISR. This avoids complex management of
+ the enable bit.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3b0e4fd74d6c6e5ca972d7341c7155663c230e86
+Author: Keith Packard <keithp@keithp.com>
+Date: Wed Nov 9 16:28:21 2022 -0800
+
+ drivers/ublox: Clean up some debug mode code
+
+ Not used in production, so we didn't catch a couple of type errors.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1dfa369085208a37f1b9c4d42ca85c261abbb1f2
+Author: Keith Packard <keithp@keithp.com>
+Date: Wed Nov 9 16:02:55 2022 -0800
+
+ telemetrum-v4.0: Remove unused file
+
+ Not sure what 'telemetrum.c' was supposed to do, but it's not used.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 543b6d08617fccc55e7dab142017969e5b565690
+Author: Bdale Garbee <bdale@gag.com>
+Date: Wed Nov 9 09:06:17 2022 -0700
+
+ altosui: force Locale.ROOT during CSV export to avoid commas in decimals
+
+commit c035f20392828c60da75d092eea4d64736316c7b
+Author: Bdale Garbee <bdale@gag.com>
+Date: Wed Nov 9 08:27:28 2022 -0700
+
+ TeleMetrum v4.0 work in progress
+
+commit 09a2575840f95f9bce6c1da25b04d297455b1487
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Nov 8 16:25:35 2022 -0800
+
+ telemetrum-v4.0: Add beeper and fix ADC defs
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9c556954d25daa6fda6771ca6d011d76b5d17524
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Nov 8 16:20:07 2022 -0800
+
+ metro-m0: Hook up ADC sample from A pins
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7bcde16c96f05595969bceef76905aa2e285c66b
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Nov 8 16:18:57 2022 -0800
+
+ samd21: Hook up AltOS style ADC
+
+ Can't use DMA; samd21 only does ADC in continuous pins. ISR per ADC,
+ manually step through.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5ca0ed94f30943952b9c63ee558733d37c896b5f
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Nov 8 16:17:58 2022 -0800
+
+ samd21: Hook up AltOS timer functionality
+
+ Need to run the data sampling stuff.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bf793f94a2218b42794fa1ae11babab8cd565a66
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Nov 8 16:17:14 2022 -0800
+
+ samd21: Move DMA channel defs to ao_dma_samd21.h
+
+ This way if we need to use DMA for something else, we've got
+ a central place to set things up.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 925d1cfb19da373249af716a6d7adc6d7450ec25
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Nov 8 16:16:40 2022 -0800
+
+ samd21: Get beep code working
+
+ Only supports TCC currently.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f354d17c7f15dcbc8c5b07fe2f950df3cb4552b2
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Nov 7 22:23:44 2022 -0800
+
+ metro-m0: Demo pin interrupts
+
+ Prints 'pressed' whenever D0 goes low.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 13f0b70a6346c9cfe04e5c494d3f34aa92e0db8b
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Nov 7 22:22:26 2022 -0800
+
+ samd21: Wire up pin interrupts
+
+ Uses the external interrupt controller. Careful about pin mapping.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ca76309700a3f6ec9775f6e6cce5915dcb4515c9
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 19:31:05 2022 -0800
+
+ altos/telemetrum-v4.0: It links
+
+ Still much work to do, but at least it links with most
+ of the pieces in place.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7ed85dc90345baec2c4143b75c4be819bb6a4d56
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 19:30:38 2022 -0800
+
+ samd21: Stub out beep code
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 62381d8582749dc2672a65cb6e7c5b8a404a3b45
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 19:30:27 2022 -0800
+
+ samd21: Stub out exti code
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b24faa163ede640305f0660c30d84e558bd2b909
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 19:29:41 2022 -0800
+
+ samd21: Shrink default stack to 512 bytes
+
+ Kinda needed to get TM v4.0 to fit.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6a44cf7f77f0b2e098ef1afc4628898234bf7fb6
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 18:33:45 2022 -0800
+
+ altos/telemetrum-v4.0: Almost building
+
+ Pin config looks good now, need to get beep and exti drivers
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f3d95af6866018bf8d472bae192907efcbe5aaa1
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 18:33:14 2022 -0800
+
+ samd21: Get serial driver building
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a06c84a27bd760039c522460f79bfe242d2d22c8
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 18:31:42 2022 -0800
+
+ samd21: Add SPI configurations used by TM v4.0
+
+ SPI 0 on PA04 PA05 PA06
+ SPI 3 on PA22 PA23 PA20
+ SPI 5 on PB22 PB23 PB03
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6e895d310224e612f0294bc953e24ffbe8d29437
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 18:31:16 2022 -0800
+
+ samd21: Get ADC code compiling
+
+ This probably isn't complete yet, but it builds
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 79613a403cbb545672afc7b169b3a833d587903a
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 18:30:31 2022 -0800
+
+ altos/ms5607: Use ao_gpio_set instead of stm_gpio_set
+
+ The former is architecture independent.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 30149c4764984972eb5d482cfc51657d4ed09ed1
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 17:47:57 2022 -0800
+
+ metro-m0: Configure on-board SPI flash part
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9481e33348f098f3df73006641b9a18a04f2c482
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 17:46:56 2022 -0800
+
+ samd21: Add ability to use arbitrary pin configs with SPI
+
+ This embeds the DOPO and DIPO configs in the address
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5d18ef0a25248f0192c3b539cad36d3de60a6f32
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 15:31:50 2022 -0800
+
+ samd21: Macro-ize the DMA register settings a bit
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 61a3971b7bda2c5a165088befe55467c09292090
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 15:12:33 2022 -0800
+
+ snekboard: Need DMA for SPI now.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 183e1bbde14cc33f1e9cc3857dbfc60d2c0c5b82
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 15:11:31 2022 -0800
+
+ samd21: Use DMA for SPI send
+
+ Appears to work even
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e5c84201f2a8968ed60941edbc6756c7dbda8a06
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 15:10:31 2022 -0800
+
+ samd21: Fix value defines to be unsigned long
+
+ This makes the compiler happy
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cbff1483ae7469d3961341a1cfdfeb8b4bb557c1
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 15:09:55 2022 -0800
+
+ samd21: Avoid divides in boot loader
+
+ The divide code is huge and overflows flash.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4839569abef7f1d9592a9da5d40fe67dcc048ce1
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 15:08:56 2022 -0800
+
+ samd21: Fix up DMA driver
+
+ Surprisingly few changes, just some function signatures and a few bit
+ twiddles.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4be4ffe72c5f678a66658e9e61d3cfac7f20a9b3
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Nov 6 15:07:46 2022 -0800
+
+ altos: Add metro-m0 port for SAMD21G18 testing
+
+ Useful as a test target
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 13a9247afabfaf93ebf46f9a33862864d2772074
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 5 21:05:13 2022 -0700
+
+ samd21: Configure ROM/RAM values per device and provide linker scripts for each
+
+ SAMD21 has 4-32k of RAM and 32-256K of ROM
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 608640858d0d02ac06fca22aa3b9db9c89733adb
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 5 20:53:23 2022 -0700
+
+ This is what I'm using locally for flashing samd21 parts
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fdc1facebf1d2818394b155a91b68e06db198728
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Nov 5 15:18:27 2022 -0700
+
+ snekboard: Add samd21 SPI test function
+
+ Just sends 'hello'
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a54f4fae343b7524db508dc1a29d36847a2decde
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Oct 23 16:33:28 2022 -0700
+
+ altos: Get SAMD21 SPI driver working in non-DMA mode
+
+ Only sercom0, and only with fixed pin configurations
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ba28c5e7f6aa3d41abc1f598dbc3c2689c9bef9c
+Author: Bdale Garbee <bdale@gag.com>
+Date: Fri Nov 4 03:15:08 2022 -0600
+
+ ao-tools: increase robustness of turnon_easymotor
+
+commit 1066699ea2e0377af67b3b0c32ec1b023dea0719
+Author: Bdale Garbee <bdale@gag.com>
+Date: Fri Nov 4 02:56:31 2022 -0600
+
+ ao-bringup: final production test script for EasyMotor v3
+
+commit a2fdfd4f9c92c1d3331f8be0e15ae19209b4ee15
+Merge: 28fd8e6f 4a1f441b
+Author: Bdale Garbee <bdale@gag.com>
+Date: Fri Nov 4 02:41:43 2022 -0600
+
+ Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 28fd8e6f157c37b08b6a17dae664597f01e8aa8a
+Author: Bdale Garbee <bdale@gag.com>
+Date: Fri Nov 4 02:41:11 2022 -0600
+
+ ao-tools: new ao-test-pressure utility for bench testing EasyMotor
+
+commit fddda6bbe5accb4868ee9bbc337937147da92ad8
+Author: Bdale Garbee <bdale@gag.com>
+Date: Fri Nov 4 02:40:53 2022 -0600
+
+ update Releasing to include EasyMini v3.0 loader
+
+commit bcc15fb429724956e72f06a4f4b1002b1def7a94
+Author: Bdale Garbee <bdale@gag.com>
+Date: Fri Nov 4 02:39:34 2022 -0600
+
+ ao-tools: don't do 'm 0' after 'E 0' since EasyMotor doesn't support it
+
+commit 4a1f441b746d4c9d1cb4a52401d76b5e81ba6f48
+Merge: e5feac1a d80c1317
+Author: Bdale Garbee <bdale@gag.com>
+Date: Wed Nov 2 11:34:35 2022 -0600
+
+ Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit e5feac1a3db00816e82cd6b1b4dbde62e40d16a6
+Author: Bdale Garbee <bdale@gag.com>
+Date: Wed Nov 2 11:33:29 2022 -0600
+
+ EasyMotor v3 production script working, sans pressure input test
+
+commit d80c1317f596f73d192e6fbb33c3579d54802182
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue Nov 1 13:00:22 2022 -0700
+
+ Add option to beep max height in feet rather than just meters
+
+ Available on all products, plus config UI changes.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d999b56de8775cd5c2f98f913e555febd92357a9
+Author: Bdale Garbee <bdale@gag.com>
+Date: Tue Nov 1 10:41:03 2022 -0600
+
+ easymini v3.0 production scripts working
+
+commit 274522bc26910051e30aaebd78e96794161b87e8
+Author: Bdale Garbee <bdale@gag.com>
+Date: Fri Oct 28 23:43:40 2022 -0600
+
+ update Releasing with tweaks made during 1.9.12 release process
+
+commit a6c32739c75d8c0369617f575a83d70031faf36a
+Author: Bdale Garbee <bdale@gag.com>
+Date: Fri Oct 28 23:10:24 2022 -0600
+
+ updating changelog for 1.9.12 release
+
commit bdabc6814b066a9b17a6557910f21b1ed03de3f0
Merge: 37dda254 c5e8826a
Author: Bdale Garbee <bdale@gag.com>
fix mailing list reference to use mailman3 path
+commit 975e3133e25c6fb5ea0c0b2487ea34e528660a45
+Merge: 05278307 bda33b76
+Author: Bdale Garbee <bdale@gag.com>
+Date: Fri Oct 28 21:49:16 2022 -0600
+
+ Merge branch 'samd21' of ssh://git.gag.com/scm/git/fw/altos into samd21
+
commit edef072dd5cffdbd3e8346719a81808b17bdc570
Author: Keith Packard <keithp@keithp.com>
Date: Thu Oct 27 21:05:10 2022 -0700
Signed-off-by: Keith Packard <keithp@keithp.com>
+commit bda33b7697456516c3022f91d2119a56448b42bb
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 17:34:56 2022 -0700
+
+ altos/snekboard: Indicate that USB is used for stdio
+
+ This isn't the default for some reason?
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 59f08a3079ccecaeb91b637e7d311f3f98d10c42
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 17:34:11 2022 -0700
+
+ altos/samd21: Automatically erase flash when writing
+
+ Need to erase pages before writing them.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f7e6cbeaf3e84935b38da8605a6d5bb702eff3a5
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 17:33:09 2022 -0700
+
+ altos/samd21: Get USB driver working in AltOS
+
+ Make sure it deals with the stdio mux, enable the clock for the GPIO
+ device.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0585dd012a9f76f1ad0f3a85d03075125704dfd3
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 17:32:35 2022 -0700
+
+ altos/samd21: Add register definitions
+
+ This adds symbols for all of the device registers.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c4a63617f0eb825a295ae8ee1ee0caf4580b7a13
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 17:31:27 2022 -0700
+
+ ao-tools: Add ao-flash-samd21 script
+
+ This takes an .elf or .ihx file and flashes that to a SAMD21 based
+ board using openocd. Note that you must have a hacked version of
+ OpenOCD for this to work via the ST-link device.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4d116df20ae2d13bbc29344a91e557448cd8c19b
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 13:57:08 2022 -0700
+
+ altos: Add sample samd21 application for snekboard
+
+ This runs a primitive application and flash loader on snekboard for
+ samd21 development.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c59892cd337162c63e5d7ba8e8eec779d201022d
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 13:55:43 2022 -0700
+
+ altos: Add SAMD21 bits
+
+ Brought back from snek, these bits provide Altos support for the Atmel
+ SAMD21 family of chips.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3a7ae9e964c9554a667a09d6b437fef136bc0fa0
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 13:55:04 2022 -0700
+
+ altos: Use --gc-sections
+
+ This allows the linker to discard unused functions.
+
+ Don't use -ffunctions-sections and -fdata-sections as that eliminates
+ several optimization opportunities and increases code size.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1523a986a7c2e8402c628e2697e44fe6281c164e
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 13:54:24 2022 -0700
+
+ altos: Switch LED pin number to 8 bits
+
+ This assumes each port has fewer than 256 pins and avoids some
+ compiler warnings
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
commit 48a8ae2d38ecad5aa84b2158045c6a2fd8abe5ab
Author: Keith Packard <keithp@keithp.com>
Date: Sun Oct 23 16:48:05 2022 -0700
Signed-off-by: Keith Packard <keithp@keithp.com>
+commit 05278307f5e9fd5f8b1a53078e6f3b125785b019
+Author: Bdale Garbee <bdale@gag.com>
+Date: Sat Oct 15 21:40:50 2022 -0600
+
+ first stab at support for TeleMetrum v4.0
+
+commit 01811a649548987d3c5c29ca13725717d090502b
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 17:34:56 2022 -0700
+
+ altos/snekboard: Indicate that USB is used for stdio
+
+ This isn't the default for some reason?
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 38a40c850716c545004dae01412b3350e5ef8cfd
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 17:34:11 2022 -0700
+
+ altos/samd21: Automatically erase flash when writing
+
+ Need to erase pages before writing them.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bd088d263172dbe9ffe399a93a836cac26a37708
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 17:33:09 2022 -0700
+
+ altos/samd21: Get USB driver working in AltOS
+
+ Make sure it deals with the stdio mux, enable the clock for the GPIO
+ device.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4a6a5de714f346b7c57c77e5f6712d39c2042479
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 17:32:35 2022 -0700
+
+ altos/samd21: Add register definitions
+
+ This adds symbols for all of the device registers.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit be0a7f000250704bcdbad82aa4ef4d9966fc6307
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 17:31:27 2022 -0700
+
+ ao-tools: Add ao-flash-samd21 script
+
+ This takes an .elf or .ihx file and flashes that to a SAMD21 based
+ board using openocd. Note that you must have a hacked version of
+ OpenOCD for this to work via the ST-link device.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c869985188ce9d28c332e449b36c4c88077cd587
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 13:57:08 2022 -0700
+
+ altos: Add sample samd21 application for snekboard
+
+ This runs a primitive application and flash loader on snekboard for
+ samd21 development.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5e7e4303f161e38ac2d84110dccafc67b3aa7fc8
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 13:55:43 2022 -0700
+
+ altos: Add SAMD21 bits
+
+ Brought back from snek, these bits provide Altos support for the Atmel
+ SAMD21 family of chips.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 643a1f736d2e57c577458d0e4d2948718d90b301
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 13:55:04 2022 -0700
+
+ altos: Use -ffunction-sections -fdata-sections
+
+ This allows the linker to discard unused functions.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c1f7bf876a67dc176a7a24b241c6dd3fe780082d
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Sep 25 13:54:24 2022 -0700
+
+ altos: Switch LED pin number to 8 bits
+
+ This assumes each port has fewer than 256 pins and avoids some
+ compiler warnings
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
commit 3735e1eb27afb873d60164a79a9e2769dc92b3a3
Author: Keith Packard <keithp@keithp.com>
Date: Sat Sep 24 15:54:20 2022 -0700
- 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"
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 \
*/
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() {
}
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() {
}
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() {
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); }
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); }
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); }
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); }
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() {
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,
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,",");
}
}
}
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);
*/
}
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");
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");
/* HAS_RADIO_10MW */
public int radio_10mw;
+ public int report_feet;
+
/* Storage info replies */
public int storage_size;
public int storage_erase_unit;
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-"))
radio_10mw = AltosLib.MISSING;
+ report_feet = AltosLib.MISSING;
+
tracker_motion = AltosLib.MISSING;
tracker_interval = AltosLib.MISSING;
/* 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:");
return false;
if (product.startsWith("TeleMetrum-v3"))
return false;
+ if (product.startsWith("TeleMetrum-v4"))
+ return true;
if (product.startsWith("EasyMega"))
return false;
return true;
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"))
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"))
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();
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);
}
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);
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);
}
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
return data32(16);
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
return data16(14);
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
return data32(20);
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
return data16(16);
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
return data32(24);
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
return data16(18);
switch (log_format) {
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
return AltosLib.model_mpu6000;
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
+ return AltosLib.model_bmi088;
}
return AltosLib.MISSING;
}
private boolean sensor_normalized() {
switch (log_format) {
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
return true;
}
return false;
private int mag_model() {
switch (log_format) {
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
return AltosLib.model_mmc5983;
}
return AltosLib.MISSING;
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
record = new AltosEepromRecordMega(eeprom);
break;
case AltosLib.AO_LOG_FORMAT_TELEMETRUM:
public static final double counts_per_g_mpu = 2048.0;
public static final double counts_per_g_bmx = 2048.0;
public static final double counts_per_g_adxl = 20.5;
+ public static final double counts_per_g_bmi088 = 1365.0;
private static double counts_per_g(int imu_type, int imu_model) {
switch (imu_model) {
return counts_per_g_adxl;
case AltosLib.model_bmx160:
return counts_per_g_bmx;
+ case AltosLib.model_bmi088:
+ return counts_per_g_bmi088;
}
switch (imu_type) {
public static final double GYRO_FULLSCALE_DEGREES_BMX = 2000.0;
public static final double GYRO_COUNTS_BMX = 32767.0;
public static final double counts_per_degree_bmx = GYRO_COUNTS_BMX / GYRO_FULLSCALE_DEGREES_BMX;
+ public static final double counts_per_degree_bmi088 = 16.384;
private static double counts_per_degree(int imu_type, int imu_model) {
switch (imu_model) {
return counts_per_degree_mpu;
case AltosLib.model_bmx160:
return counts_per_degree_bmx;
+ case AltosLib.model_bmi088:
+ return counts_per_degree_bmi088;
}
switch (imu_type) {
}
return true;
}
+ if (line.startsWith("BMI088:")) {
+ String[] items = line.split("\\s+");
+
+ imu_model = AltosLib.model_bmi088;
+
+ if (items.length >= 7) {
+ accel_along = Integer.parseInt(items[1]);
+ accel_across = Integer.parseInt(items[2]);
+ accel_through = Integer.parseInt(items[3]);
+ gyro_roll = Integer.parseInt(items[4]);
+ gyro_pitch = Integer.parseInt(items[5]);
+ gyro_yaw = Integer.parseInt(items[6]);
+ }
+ return true;
+ }
return false;
}
AltosIdler.idle_ms5607,
AltosIdler.idle_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,
AltosIdler.idle_ms5607,
AltosIdler.idle_imu, AltosIdler.idle_mag,
AltosIdler.idle_sensor_mega),
+ new AltosIdler("TeleMega-v6",
+ AltosIdler.idle_gps,
+ AltosIdler.idle_adxl375,
+ AltosIdler.idle_ms5607,
+ AltosIdler.idle_imu, AltosIdler.idle_mag,
+ AltosIdler.idle_sensor_mega),
new AltosIdler("EasyMega-v1",
AltosIdler.idle_mma655x,
AltosIdler.idle_ms5607,
public static final int AO_LOG_FORMAT_TELEMEGA_4 = 19;
public static final int AO_LOG_FORMAT_EASYMOTOR = 20;
public static final int AO_LOG_FORMAT_TELEMEGA_5 = 21;
+ public static final int AO_LOG_FORMAT_TELEMEGA_6 = 22;
public static final int AO_LOG_FORMAT_NONE = 127;
public static final int model_mpu6000 = 0;
public static final int model_bmx160 = 3;
public static final int model_hmc5883 = 4;
public static final int model_mmc5983 = 5;
+ public static final int model_bmi088 = 6;
public static boolean isspace(int c) {
switch (c) {
return product_easymotor;
case AO_LOG_FORMAT_TELEMEGA_5:
return product_telemega;
+ case AO_LOG_FORMAT_TELEMEGA_6:
+ return product_telemega;
case AO_LOG_FORMAT_NONE:
return product_altusmetrum;
default:
final static int packet_type_mini3 = 0x11;
final static int packet_type_mega_sensor_bmx160 = 0x12;
final static int packet_type_mega_norm_mpu6000_mmc5983 = 0x13;
+ final static int packet_type_mega_norm_bmi088_mmc5983 = 0x14;
static AltosTelemetry parse_hex(String hex) throws ParseException, AltosCRCException {
AltosTelemetry telem = null;
case packet_type_mega_norm_mpu6000_mmc5983:
telem = new AltosTelemetryMegaNorm(bytes, AltosLib.model_mpu6000, AltosLib.model_mmc5983);
break;
+ case packet_type_mega_norm_bmi088_mmc5983:
+ telem = new AltosTelemetryMegaNorm(bytes, AltosLib.model_bmi088, AltosLib.model_mmc5983);
+ break;
default:
telem = new AltosTelemetryRaw(bytes);
break;
Declare new USB ids
Declare new Product name
Add item to product_name function
+ Add entry in product_id_from_log_format
+ Declare new sensor model
2. AltosIdleFetch.java
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
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;
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;
"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;
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);
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;
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;
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)
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"
--- /dev/null
+#!/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
+++ /dev/null
-#!/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
--- /dev/null
+#!/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
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"
#
# 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
sleep 1
-./test-easymini-v1.0
+./test-easymini-v3.0
exit $?
--- /dev/null
+#!/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 $?
#!/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
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 $?
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
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;
}
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,
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:
-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
openocd \
-f interface/stlink-v2.cfg \
-f target/lpc11xx.cfg \
+ -c 'adapter speed 1000' \
-f $cmds \
-c shutdown
--- /dev/null
+#!/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"
--- /dev/null
+.\"
+.\" Copyright © 2022 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-FLASH-LPC 1 "ao-flash-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
--- /dev/null
+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
--- /dev/null
+.\"
+.\" 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
--- /dev/null
+/*
+ * 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);
+}
#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 {
/* 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;
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);
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'`
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
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 \
:stylesheet: am.css
:linkcss:
:numbered:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
+extends: default
base:
font_family: Open Sans Light
+ font-size: 12
heading:
font_color: #78079a
font_size: 17
font:
catalog:
+ merge: true
Open Sans Light:
normal: OpenSans-Light.ttf
italic: OpenSans-LightItalic.ttf
page:
background_color: ffffff
layout: portrait
- margin: [0.5in, 0.67in, 0.75in, 0.67in]
+ margin: [0.5in, 0.67in, 0.67in, 0.67in]
size: letter
footer:
height: 0.5in
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
: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
:easytimer: 1
:easymotor: 1
:application: AltosUI
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
:copyright: Bdale Garbee and Keith Packard 2018
:stylesheet: am.css
:linkcss:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
:toc:
[appendix]
== Release Notes
+ :leveloffset: 2
+ include::release-notes-1.9.13.adoc[]
+
+ <<<<
:leveloffset: 2
include::release-notes-1.9.12.adoc[]
:altusmetrum: 1
:easymini: 1
:application: AltosUI
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
[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]
:doctype: article
:stylesheet: am-notoc.css
:linkcss:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
== The Google Maps Problem
:toc:
:doctype: book
:numbered:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
== 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
:toc:
:motortest: 1
:application: Motor Testing
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
:doctype: article
:stylesheet: am-notoc.css
:linkcss:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
--- /dev/null
+= 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.
[appendix]
== Release Notes
+ :leveloffset: 2
+ include::release-notes-1.9.13.adoc[]
+
+ <<<<
:leveloffset: 2
include::release-notes-1.9.12.adoc[]
[appendix]
== Release Notes
+ :leveloffset: 2
+ include::release-notes-1.9.13.adoc[]
+
+ <<<<
:leveloffset: 2
include::release-notes-1.9.12.adoc[]
:radio: 1
:gps: 1
:application: TeleGPS
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
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.
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
:toc:
:telelaunch: 1
:application: TeleLaunch
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
:doctype: article
:toc:
:numbered:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
int serial;
char name[256];
char path[256];
+ int (*method_1)(int x, int y);
//%mutable;
};
%{
#include "libaltos.h"
%}
-
+%extend altos_device {
+ int method_1(int x, int y) {
+ return ($self->method_1)(x, y);
+ }
+}
telemega-v3.0 telemega-v3.0/flash-loader \
telemega-v4.0 telemega-v4.0/flash-loader \
telemega-v5.0 telemega-v5.0/flash-loader \
+ telemega-v6.0 telemega-v6.0/flash-loader \
telemetrum-v2.0 telemetrum-v2.0/flash-loader \
telemetrum-v3.0 telemetrum-v3.0/flash-loader \
telegps-v0.3 telegps-v0.3/flash-loader \
PICOLIBC_CFLAGS= \
-specs=picolibc.specs \
+ -Wl,--gc-sections \
$(PICOLIBC_PRINTF_CFLAGS)
AO_CFLAGS=\
#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);
/// Buffer to hold the message portion of the AX.25 packet as we prepare it.
static uint8_t tncBuffer[TNC_BUFFER_SIZE];
+#pragma GCC diagnostic ignored "-Wformat-overflow="
/**
* Initialize the TNC internal variables.
*/
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_bmi088.h>
+#include <ao_data.h>
+
+#define AO_BMI088_SPI_SPEED ao_spi_speed(100000)
+
+#define ao_bmi088_spi_get() ao_spi_get(AO_BMI088_SPI_BUS, AO_BMI088_SPI_SPEED)
+#define ao_bmi088_spi_put() ao_spi_put(AO_BMI088_SPI_BUS)
+
+#define ao_bmi088_acc_start() ao_spi_set_cs(AO_BMI088_ACC_CS_PORT, \
+ (1 << AO_BMI088_ACC_CS_PIN))
+#define ao_bmi088_acc_end() ao_spi_clr_cs(AO_BMI088_ACC_CS_PORT, \
+ (1 << AO_BMI088_ACC_CS_PIN))
+#define ao_bmi088_gyr_start() ao_spi_set_cs(AO_BMI088_GYR_CS_PORT, \
+ (1 << AO_BMI088_GYR_CS_PIN))
+#define ao_bmi088_gyr_end() ao_spi_clr_cs(AO_BMI088_GYR_CS_PORT, \
+ (1 << AO_BMI088_GYR_CS_PIN))
+
+static uint8_t
+_ao_bmi088_acc_reg_read(uint8_t addr)
+{
+ uint8_t v[2];
+ addr |= 0x80;
+ ao_bmi088_acc_start();
+ ao_spi_send(&addr, 1, AO_BMI088_SPI_BUS);
+ ao_spi_recv(v, 2, AO_BMI088_SPI_BUS); /* part sends garbage for first byte */
+ ao_bmi088_acc_end();
+ return v[1];
+}
+
+static void
+_ao_bmi088_acc_reg_write(uint8_t addr, uint8_t value)
+{
+ uint8_t d[2] = { addr, value };
+ ao_bmi088_acc_start();
+ ao_spi_send(d, 2, AO_BMI088_SPI_BUS);
+ ao_bmi088_acc_end();
+}
+
+static void
+_ao_bmi088_acc_sample_read(struct ao_bmi088_acc_sample *sample)
+{
+ uint8_t dummy;
+ uint8_t addr = BMI088_ACC_DATA | 0x80;
+
+ ao_bmi088_acc_start();
+ ao_spi_send(&addr, 1, AO_BMI088_SPI_BUS);
+ ao_spi_recv(&dummy, 1, AO_BMI088_SPI_BUS); /* part sends garbage for first byte */
+ ao_spi_recv(sample, sizeof(struct ao_bmi088_acc_sample), AO_BMI088_SPI_BUS);
+ ao_bmi088_acc_end();
+}
+
+static void
+_ao_bmi088_gyr_read(uint8_t addr, void *data, uint8_t len)
+{
+ addr |= 0x80;
+ ao_bmi088_gyr_start();
+ ao_spi_send(&addr, 1, AO_BMI088_SPI_BUS);
+ ao_spi_recv(data, len, AO_BMI088_SPI_BUS);
+ ao_bmi088_gyr_end();
+}
+
+static uint8_t
+_ao_bmi088_gyr_reg_read(uint8_t addr)
+{
+ uint8_t v;
+ _ao_bmi088_gyr_read(addr, &v, 1);
+ return v;
+}
+
+static void
+_ao_bmi088_gyr_reg_write(uint8_t addr, uint8_t value)
+{
+ uint8_t d[2] = { addr, value };
+ ao_bmi088_gyr_start();
+ ao_spi_send(d, 2, AO_BMI088_SPI_BUS);
+ ao_bmi088_gyr_end();
+}
+
+static void
+_ao_bmi088_gyr_sample_read(struct ao_bmi088_gyr_sample *sample)
+{
+ _ao_bmi088_gyr_read(BMI088_GYRO_DATA, sample, sizeof (struct ao_bmi088_gyr_sample));
+}
+
+static void
+ao_bmi088_reset(void)
+{
+ ao_bmi088_spi_get();
+
+ /* reset the two devices */
+ _ao_bmi088_acc_reg_write(BMI088_ACC_SOFTRESET, BMI088_ACC_SOFTRESET_RESET);
+ _ao_bmi088_gyr_reg_write(BMI088_GYRO_SOFTRESET, BMI088_GYRO_SOFTRESET_RESET);
+
+ /* wait 30ms (that's how long the gyro takes */
+ ao_delay(AO_MS_TO_TICKS(30));
+
+ /* force acc part to SPI mode */
+ ao_bmi088_acc_start();
+
+ ao_delay(AO_MS_TO_TICKS(1));
+
+ ao_bmi088_acc_end();
+
+ ao_bmi088_spi_put();
+}
+
+static bool
+ao_bmi088_accel_good(int16_t pos, int16_t neg, int32_t good)
+{
+ int32_t diff = (int32_t) pos - (int32_t) neg;
+
+ return diff >= good;
+}
+
+static void
+ao_bmi088_setup(void)
+{
+ bool working = false;
+ struct ao_bmi088_acc_sample acc_pos, acc_neg;
+
+ ao_bmi088_reset();
+
+ ao_bmi088_spi_get();
+
+ /* Make sure the two devices are alive */
+ if (_ao_bmi088_acc_reg_read(BMI088_ACC_CHIP_ID) != BMI088_ACC_CHIP_ID_BMI088)
+ goto failure;
+
+ if (_ao_bmi088_gyr_reg_read(BMI088_GYRO_CHIP_ID) != BMI088_GYRO_CHIP_ID_BMI088)
+ goto failure;
+
+ /* Turn on the accelerometer */
+
+ _ao_bmi088_acc_reg_write(BMI088_ACC_PWR_CTRL, BMI088_ACC_PWR_CTRL_ON);
+
+ /* Wait 5ms after changing accel power mode */
+ ao_delay(AO_MS_TO_TICKS(5));
+
+ /* Accel self test. Procedure:
+ *
+ * 1) Set ±24g range by writing 0x03 to register ACC_RANGE (0x41)
+ * 2) Set ODR=1.6kHz, continuous sampling mode, “normal mode” (norm_avg4) by writing 0xA7 to
+ * register ACC_CONF (0x40)
+ * • Continuous filter function: set bit7 in ACC_CONF
+ * • “normal avg4 mode”: ACC_CONF |= 0x02<<4
+ * • ODR=1.6kHz: ACC_CONF |= 0x0C
+ * 3) Wait for > 2 ms
+ * 4) Enable the positive self-test polarity (i.e. write 0x0D to register ACC_SELF_TEST (0x6D))
+ * 5) Wait for > 50ms
+ * 6) Read the accelerometer offset values for each axis (positive self-test response)
+ * 7) Enable the negative self-test polarity (i.e. write 0x09 to register ACC_SELF_TEST (0x6D))
+ * 8) Wait for > 50ms
+ * 9) Read the accelerometer offset values for each axis (negative self-test response)
+ * 10) Disable the self-test (i.e. write 0x00 to register ACC_SELF_TEST (0x6D))
+ * 11) Calculate difference of positive and negative self-test response and compare with the expected
+ * values (see table below)
+ * 12) Wait for > 50ms to let the sensor settle to normal mode steady state operation
+ */
+
+ _ao_bmi088_acc_reg_write(BMI088_ACC_RANGE, BMI088_ACC_RANGE_24);
+
+ _ao_bmi088_acc_reg_write(BMI088_ACC_CONF,
+ (BMI088_ACC_CONF_BWP_NORMAL << BMI088_ACC_CONF_BWP) |
+ (BMI088_ACC_CONF_ODR_1600 << BMI088_ACC_CONF_ODR));
+
+ ao_delay(AO_MS_TO_TICKS(2));
+
+ _ao_bmi088_acc_reg_write(BMI088_ACC_SELF_TEST, BMI088_ACC_SELF_TEST_POSITIVE);
+
+ ao_delay(AO_MS_TO_TICKS(50));
+
+ _ao_bmi088_acc_sample_read(&acc_pos);
+
+ _ao_bmi088_acc_reg_write(BMI088_ACC_SELF_TEST, BMI088_ACC_SELF_TEST_NEGATIVE);
+
+ ao_delay(AO_MS_TO_TICKS(50));
+
+ _ao_bmi088_acc_sample_read(&acc_neg);
+
+ _ao_bmi088_acc_reg_write(BMI088_ACC_SELF_TEST, BMI088_ACC_SELF_TEST_OFF);
+
+ /* Self test X and Y must show at least 1000 mg difference,
+ * Z must show at least 500mg difference
+ */
+ if (!ao_bmi088_accel_good(acc_pos.x, acc_neg.x, (int32_t) ao_bmi_accel_to_sample(GRAVITY)))
+ goto failure;
+
+ if (!ao_bmi088_accel_good(acc_pos.y, acc_neg.y, (int32_t) ao_bmi_accel_to_sample(GRAVITY)))
+ goto failure;
+
+ if (!ao_bmi088_accel_good(acc_pos.z, acc_neg.z, (int32_t) ao_bmi_accel_to_sample(GRAVITY/2)))
+ goto failure;
+
+ /*
+ * Self-test gyro
+ *
+ * To trigger the self-test, bit #0 (‘bite_trig’) in address
+ * GYRO_SELF_TEST must be set. When the test is finished, bit
+ * #1 (‘bist_rdy’) will be set by the gyro and the test result
+ * can then be found in bit #2 (‘bist_fail’). A ‘0’ indicates
+ * that the test was passed without issues. If a failure
+ * occurred, the bit ‘bist_fail’ will be set to ‘1’.
+ */
+
+ _ao_bmi088_gyr_reg_write(BMI088_GYRO_SELF_TEST,
+ (1 << BMI088_GYRO_SELF_TEST_TRIG_BIST));
+
+ uint8_t gyro_self_test;
+ for(;;) {
+ gyro_self_test = _ao_bmi088_gyr_reg_read(BMI088_GYRO_SELF_TEST);
+ if ((gyro_self_test & (1 << BMI088_GYRO_SELF_TEST_BIST_RDY)) != 0)
+ break;
+ }
+ if ((gyro_self_test & (1 << BMI088_GYRO_SELF_TEST_BIST_FAIL)) != 0)
+ goto failure;
+
+
+ /* Put accel in desired mode */
+
+ /* 200 Hz sampling rate, normal filter */
+ _ao_bmi088_acc_reg_write(BMI088_ACC_CONF,
+ (BMI088_ACC_CONF_BWP_NORMAL << BMI088_ACC_CONF_BWP) |
+ (BMI088_ACC_CONF_ODR_200 << BMI088_ACC_CONF_ODR));
+
+
+ /* 24 g range */
+ _ao_bmi088_acc_reg_write(BMI088_ACC_RANGE, BMI088_ACC_RANGE_24);
+
+ /* Put gyro in desired mode */
+
+ /* 2000°/s range */
+ _ao_bmi088_gyr_reg_write(BMI088_GYRO_RANGE, BMI088_GYRO_RANGE_2000);
+
+ /* 200 Hz sampling rate, 64Hz filter */
+ _ao_bmi088_gyr_reg_write(BMI088_GYRO_BANDWIDTH, BMI088_GYRO_BANDWIDTH_200_64);
+
+ working = true;
+failure:
+ if (!working)
+ AO_SENSOR_ERROR(AO_DATA_BMI088);
+
+ ao_bmi088_spi_put();
+}
+
+struct ao_bmi088_sample ao_bmi088_current;
+
+static void
+ao_bmi088(void)
+{
+ struct ao_bmi088_sample sample;
+
+ ao_bmi088_setup();
+ for (;;)
+ {
+ ao_bmi088_spi_get();
+ _ao_bmi088_acc_sample_read(&sample.acc);
+ _ao_bmi088_gyr_sample_read(&sample.gyr);
+ ao_bmi088_spi_put();
+ ao_arch_block_interrupts();
+ ao_bmi088_current = sample;
+ AO_DATA_PRESENT(AO_DATA_BMI088);
+ AO_DATA_WAIT();
+ ao_arch_release_interrupts();
+ }
+}
+
+static struct ao_task ao_bmi088_task;
+
+static void
+ao_bmi088_show(void)
+{
+#ifdef AO_LOG_NORMALIZED
+ printf ("BMI088: %7d %7d %7d %7d %7d %7d\n",
+ ao_bmi088_along(&ao_bmi088_current),
+ ao_bmi088_across(&ao_bmi088_current),
+ ao_bmi088_through(&ao_bmi088_current),
+ ao_bmi088_roll(&ao_bmi088_current),
+ ao_bmi088_pitch(&ao_bmi088_current),
+ ao_bmi088_yaw(&ao_bmi088_current));
+#else
+ printf ("Accel: %7d %7d %7d Gyro: %7d %7d %7d\n",
+ ao_bmi088_current.acc.x,
+ ao_bmi088_current.acc.y,
+ ao_bmi088_current.acc.z,
+ ao_bmi088_current.gyr.x,
+ ao_bmi088_current.gyr.y,
+ ao_bmi088_current.gyr.z);
+#endif
+}
+
+static const struct ao_cmds bmi_cmds[] = {
+ { ao_bmi088_show, "I\0Show BMI088 status" },
+ { 0, 0 }
+};
+
+void
+ao_bmi088_init(void)
+{
+ AO_TICK_TYPE then;
+ ao_spi_init_cs(AO_BMI088_ACC_CS_PORT, (1 << AO_BMI088_ACC_CS_PIN));
+ ao_spi_init_cs(AO_BMI088_GYR_CS_PORT, (1 << AO_BMI088_GYR_CS_PIN));
+
+ /* force acc part to SPI mode */
+ ao_bmi088_acc_start();
+ then = ao_time();
+ while ((ao_time() - then) < 2)
+ ;
+ ao_bmi088_acc_end();
+
+ ao_add_task(&ao_bmi088_task, ao_bmi088, "bmi088");
+
+ ao_cmd_register(bmi_cmds);
+}
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_BMI088_H_
+#define _AO_BMI088_H_
+
+#include <math.h>
+
+struct ao_bmi088_acc_sample {
+ int16_t x;
+ int16_t y;
+ int16_t z;
+};
+
+struct ao_bmi088_gyr_sample {
+ int16_t x;
+ int16_t y;
+ int16_t z;
+};
+
+struct ao_bmi088_sample {
+ struct ao_bmi088_acc_sample acc;
+ struct ao_bmi088_gyr_sample gyr;
+};
+
+extern struct ao_bmi088_sample ao_bmi088_current;
+
+void
+ao_bmi088_init(void);
+
+#define BMI088_ACC_CHIP_ID 0x00
+#define BMI088_ACC_CHIP_ID_BMI088 0x1e
+
+#define BMI088_ACC_ERR_REG 0x02
+# define BMI088_ACC_ERR_REG_ERROR_CODE 2
+# define BMI088_ACC_ERR_REG_FATAL_ERR 0
+
+#define BMI088_ACC_STATUS 0x03
+# define BMI088_ACC_STATUS_ACC_DRDY 7
+
+#define BMI088_ACC_DATA 0x12
+#define BMI088_ACC_TIME 0x18
+
+#define BMI088_ACC_INT_STAT_1 0x1d
+# define BMI088_ACC_INT_STAT_1_ACC_DRDY 7
+
+#define BMI088_ACC_TEMP 0x22
+
+#define BMI088_ACC_CONF 0x40
+# define BMI088_ACC_CONF_BWP 4
+# define BMI088_ACC_CONF_BWP_OSR4 0x08
+# define BMI088_ACC_CONF_BWP_OSR2 0x08
+# define BMI088_ACC_CONF_BWP_NORMAL 0x0a
+# define BMI088_ACC_CONF_ODR 0
+# define BMI088_ACC_CONF_ODR_12_5 0x05
+# define BMI088_ACC_CONF_ODR_25 0x06
+# define BMI088_ACC_CONF_ODR_50 0x07
+# define BMI088_ACC_CONF_ODR_100 0x08
+# define BMI088_ACC_CONF_ODR_200 0x09
+# define BMI088_ACC_CONF_ODR_400 0x0a
+# define BMI088_ACC_CONF_ODR_800 0x0b
+# define BMI088_ACC_CONF_ODR_1600 0x0c
+
+#define BMI088_ACC_RANGE 0x41
+# define BMI088_ACC_RANGE_3 0x00
+# define BMI088_ACC_RANGE_6 0x01
+# define BMI088_ACC_RANGE_12 0x02
+# define BMI088_ACC_RANGE_24 0x03
+
+#define BMI088_INT1_IO_CONF 0x53
+# define BMI088_INT1_IO_CONF_INT1_IN 4
+# define BMI088_INT1_IO_CONF_INT1_OUT 3
+# define BMI088_INT1_IO_CONF_INT1_OD 2
+# define BMI088_INT1_IO_CONF_INT1_LVL 1
+
+#define BMI088_INT2_IO_CONF 0x54
+# define BMI088_INT2_IO_CONF_INT2_IN 4
+# define BMI088_INT2_IO_CONF_INT2_OUT 3
+# define BMI088_INT2_IO_CONF_INT2_OD 2
+# define BMI088_INT2_IO_CONF_INT2_LVL 1
+
+#define BMI088_INT2_INT2_MAP_DATA 0x58
+# define BMI088_INT2_INT2_MAP_DATA_INT2_DRDY 6
+# define BMI088_INT2_INT2_MAP_DATA_INT1_DRDY 2
+
+#define BMI088_ACC_SELF_TEST 0x6d
+# define BMI088_ACC_SELF_TEST_OFF 0x00
+# define BMI088_ACC_SELF_TEST_POSITIVE 0x0d
+# define BMI088_ACC_SELF_TEST_NEGATIVE 0x09
+
+#define BMI088_ACC_PWR_CONF 0x7c
+# define BMI088_ACC_PWR_CONF_SUSPEND 0x03
+# define BMI088_ACC_PWR_CONF_ACTIVE 0x00
+
+#define BMI088_ACC_PWR_CTRL 0x7d
+# define BMI088_ACC_PWR_CTRL_OFF 0x00
+# define BMI088_ACC_PWR_CTRL_ON 0x04
+
+#define BMI088_ACC_SOFTRESET 0x7e
+# define BMI088_ACC_SOFTRESET_RESET 0xb6
+
+#define BMI088_GYRO_CHIP_ID 0x00
+# define BMI088_GYRO_CHIP_ID_BMI088 0x0f
+
+#define BMI088_GYRO_DATA 0x02
+#define BMI088_GYRO_DATA_X 0x02
+#define BMI088_GYRO_DATA_Y 0x04
+#define BMI088_GYRO_DATA_Z 0x06
+
+#define BMI088_GYRO_INT_STAT_1 0x0a
+# define BMI088_GYRO_INT_STAT_1_GYRO_DRDY 7
+
+#define BMI088_GYRO_RANGE 0x0f
+# define BMI088_GYRO_RANGE_2000 0x00
+# define BMI088_GYRO_RANGE_1000 0x01
+# define BMI088_GYRO_RANGE_500 0x02
+# define BMI088_GYRO_RANGE_250 0x03
+# define BMI088_GYRO_RANGE_125 0x04
+
+#define BMI088_GYRO_BANDWIDTH 0x10
+# define BMI088_GYRO_BANDWIDTH_2000_532 0x00
+# define BMI088_GYRO_BANDWIDTH_2000_230 0x01
+# define BMI088_GYRO_BANDWIDTH_1000_116 0x02
+# define BMI088_GYRO_BANDWIDTH_400_47 0x03
+# define BMI088_GYRO_BANDWIDTH_200_23 0x04
+# define BMI088_GYRO_BANDWIDTH_100_12 0x05
+# define BMI088_GYRO_BANDWIDTH_200_64 0x06
+# define BMI088_GYRO_BANDWIDTH_100_32 0x07
+
+#define BMI088_GYRO_LPM1 0x11
+# define BMI088_GYRO_LPM1_NORMAL 0x00
+# define BMI088_GYRO_LPM1_SUSPEND 0x80
+# define BMI088_GYRO_LPM1_DEEP_SUSPEND 0x20
+
+#define BMI088_GYRO_SOFTRESET 0x14
+# define BMI088_GYRO_SOFTRESET_RESET 0xb6
+
+#define BMI088_GYRO_INT_CTRL 0x15
+# define BMI088_GYRO_INT_CTRL_DISABLE 0x00
+# define BMI088_GYRO_INT_CTRL_ENABLE 0x80
+
+#define BMI088_INT3_INT4_IO_CONF 0x16
+# define BMI088_INT3_INT4_IO_CONF_INT4_OD 3
+# define BMI088_INT3_INT4_IO_CONF_INT4_LVL 2
+# define BMI088_INT3_INT4_IO_CONF_INT3_OD 1
+# define BMI088_INT3_INT4_IO_CONF_INT3_LVL 0
+
+#define BMI088_INT3_INT4_IO_MAP 0x18
+# define BMI088_INT3_INT4_IO_MAP_NONE 0x00
+# define BMI088_INT3_INT4_IO_MAP_INT3 0x01
+# define BMI088_INT3_INT4_IO_MAP_INT4 0x80
+# define BMI088_INT3_INT4_IO_MAP_INT3_INT4 0x81
+
+#define BMI088_GYRO_SELF_TEST 0x3c
+# define BMI088_GYRO_SELF_TEST_RATE_OK 4
+# define BMI088_GYRO_SELF_TEST_BIST_FAIL 2
+# define BMI088_GYRO_SELF_TEST_BIST_RDY 1
+# define BMI088_GYRO_SELF_TEST_TRIG_BIST 0
+
+#define BMI088_GYRO_FULLSCALE ((float) 2000.0f * (float) M_PI / 180.0f)
+
+static inline float
+ao_bmi088_gyro(float sensor) {
+ return sensor * ((float) (BMI088_GYRO_FULLSCALE / 32767.0));
+}
+
+#define ao_bmi_gyro_to_sample(gyro) ((gyro) * (32767.0f / (BMI088_GYRO_FULLSCALE
+
+#define BMI088_ACCEL_FULLSCALE 24
+
+static inline float
+ao_bmi088_accel(int16_t sensor) {
+ return (float) sensor * ((float) (BMI088_ACCEL_FULLSCALE * GRAVITY / 32767.0));
+}
+
+#define ao_bmi_accel_to_sample(accel) ((accel) * (32767.0f / (BMI088_ACCEL_FULLSCALE * GRAVITY)))
+
+#endif /* _AO_BMI088_H_ */
void
ao_radio_recv_abort(void)
{
+ ao_exti_disable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
ao_radio_abort = 1;
ao_wakeup(&ao_radio_wake);
}
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);
}
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);
}
#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
#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
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)
/* 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();
}
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;
}
}
/* 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();
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);
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]);
}
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;
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);
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;
#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;
* NAV-TIMEUTC message parsing
*/
static struct nav_timeutc {
+ int32_t nano;
uint16_t year;
uint8_t month;
uint8_t day;
#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 */
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)) {
#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();
void
ao_lco_set_firing(uint8_t firing);
+void
+ao_lco_pretend(void);
+
void
ao_lco_toggle_drag(void);
uint16_t ao_lco_min_box, ao_lco_max_box;
+uint8_t ao_lco_pretending;
+
#if AO_LCO_DRAG
uint8_t ao_lco_drag_race;
#endif
{
ao_lco_min_box = 0xff;
ao_lco_max_box = 0x00;
+ ao_lco_pretending = 0;
memset(ao_lco_box_mask, 0, sizeof (ao_lco_box_mask));
}
ao_lco_set_box(uint16_t new_box)
{
ao_lco_box = new_box;
- if (ao_lco_box < AO_PAD_MAX_BOXES)
- ao_lco_channels[ao_lco_box] = 0;
+ if (ao_lco_box < AO_PAD_MAX_BOXES) {
+ if (ao_lco_pretending)
+ ao_lco_channels[ao_lco_box] = 0xff;
+ else
+ ao_lco_channels[ao_lco_box] = 0;
+ }
ao_lco_pad = 1;
ao_lco_show();
}
{
int8_t r;
int8_t try;
- uint8_t box;
- uint8_t boxes = 0;
+ uint16_t box;
+ uint16_t boxes = 0;
ao_lco_box_reset_present();
ao_lco_show_box(0);
if (r == AO_RADIO_CMAC_OK) {
++boxes;
ao_lco_box_set_present(box);
- ao_lco_show_pad(boxes % 10);
+ ao_lco_show_pad((uint8_t) (boxes % 10));
ao_delay(AO_MS_TO_TICKS(30));
break;
}
ao_lco_set_box(ao_lco_min_box);
}
+void
+ao_lco_pretend(void)
+{
+ uint16_t box;
+
+ ao_lco_pretending = 1;
+ ao_lco_min_box = 1;
+ ao_lco_max_box = AO_PAD_MAX_BOXES - 1;
+ for (box = ao_lco_min_box; box < ao_lco_max_box; box++)
+ ao_lco_box_set_present(box);
+ ao_lco_box = ao_lco_min_box;
+ memset(ao_lco_valid, 0, sizeof (ao_lco_valid));
+ memset(ao_lco_channels, 0, sizeof (ao_lco_channels));
+ ao_lco_set_box(ao_lco_min_box);
+}
+
void
ao_lco_monitor(void)
{
static const struct {
void *port;
- uint16_t pin;
+ uint8_t pin;
} ao_leds[] = {
#ifdef LED_0_PORT
[0] { LED_0_PORT, LED_0_PIN },
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
--- /dev/null
+ao_product.h
+*.elf
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 \
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:
#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", \
#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_ */
#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
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;
#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
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;
}
#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)
{ "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
#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)
#if HAS_RADIO_10MW
uint8_t radio_10mw; /* minor version 25 */
#endif
+ uint8_t report_feet; /* minor version 26 */
};
struct ao_config_1_24 {
#define AO_DATA_BMX160 0
#endif
+#if HAS_BMI088
+#include <ao_bmi088.h>
+#define AO_DATA_BMI088 (1 << 2)
+#else
+#define AO_DATA_BMI088 0
+#endif
+
#ifndef HAS_SENSOR_ERRORS
#if HAS_IMU || HAS_MMA655X || HAS_MS5607 || HAS_MS5611
#define HAS_SENSOR_ERRORS 1
#ifdef AO_DATA_RING
-#define AO_DATA_ALL (AO_DATA_ADC|AO_DATA_MS5607|AO_DATA_MPU6000|AO_DATA_HMC5883|AO_DATA_MMA655X|AO_DATA_MPU9250|AO_DATA_ADXL375|AO_DATA_BMX160|AO_DATA_MMC5983)
+#define AO_DATA_ALL (AO_DATA_ADC|AO_DATA_MS5607|AO_DATA_MPU6000|AO_DATA_HMC5883|AO_DATA_MMA655X|AO_DATA_MPU9250|AO_DATA_ADXL375|AO_DATA_BMX160|AO_DATA_MMC5983|AO_DATA_BMI088)
struct ao_data {
AO_TICK_TYPE tick;
int16_t z_accel;
#endif
#endif
+#if HAS_BMI088
+ struct ao_bmi088_sample bmi088;
+#if !HAS_ADXL375
+ int16_t z_accel;
+#endif
+#endif
};
#define ao_data_ring_next(n) (((n) + 1) & (AO_DATA_RING - 1))
#endif /* 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
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)
#endif
+#if !HAS_GYRO && HAS_BMI088
+
+#define HAS_GYRO 1
+
+typedef int16_t gyro_t; /* in raw sample units */
+typedef int16_t angle_t; /* in degrees */
+
+/* X axis is aligned with the direction of motion (along) */
+/* Y axis is aligned in the other board axis (across) */
+/* Z axis is aligned perpendicular to the board (through) */
+
+static inline float ao_convert_gyro(float sensor)
+{
+ return ao_bmi088_gyro(sensor);
+}
+
+static inline float ao_convert_accel(int16_t sensor)
+{
+ return ao_bmi088_accel(sensor);
+}
+
+#endif
+
#if !HAS_MAG && HAS_HMC5883
#define HAS_MAG 1
#endif
#if HAS_BMX160
ao_data_ring[head].bmx160 = ao_bmx160_current;
+#endif
+#if HAS_BMI088
+ ao_data_ring[head].bmi088 = ao_bmi088_current;
#endif
ao_data_ring[head].tick = ao_tick_count;
ao_data_head = ao_data_ring_next(head);
#define AO_LOG_FORMAT_TELEMEGA_4 19 /* 32 byte typed telemega records with 32 bit gyro cal and Bmx160 */
#define AO_LOG_FORMAT_EASYMOTOR 20 /* 16 byte typed easymotor records with pressure sensor and adxl375 */
#define AO_LOG_FORMAT_TELEMEGA_5 21 /* 32 byte typed telemega records with 32 bit gyro cal, mpu6000 and mmc5983 */
+#define AO_LOG_FORMAT_TELEMEGA_6 22 /* 32 byte typed telemega records with 32 bit gyro cal, bmi088 and mmc5983 */
#define AO_LOG_FORMAT_NONE 127 /* No log at all */
/* Return the flight number from the given log slot, 0 if none, -slot on failure */
} u;
};
-#if AO_LOG_FORMAT == AO_LOG_FOMAT_TELEMEGA_OLD || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_3 || AO_LOG_FORMAT == AO_LOG_FORMAT_EASYMEGA_2 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_4 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_5
+#if AO_LOG_FORMAT == AO_LOG_FOMAT_TELEMEGA_OLD || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_3 || AO_LOG_FORMAT == AO_LOG_FORMAT_EASYMEGA_2 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_4 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_5 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_6
typedef struct ao_log_mega ao_log_type;
#endif
ao_log_data.u.sensor.pres = d->ms5607_raw.pres;
ao_log_data.u.sensor.temp = d->ms5607_raw.temp;
#endif
-#if HAS_MPU6000
+
#ifdef AO_LOG_NORMALIZED
+# if HAS_IMU
ao_log_data.u.sensor.accel_along = ao_data_along(d);
ao_log_data.u.sensor.accel_across = ao_data_across(d);
ao_log_data.u.sensor.accel_through = ao_data_through(d);
ao_log_data.u.sensor.gyro_roll = ao_data_roll(d);
ao_log_data.u.sensor.gyro_pitch = ao_data_pitch(d);
ao_log_data.u.sensor.gyro_yaw = ao_data_yaw(d);
-#else
+# endif
+# if HAS_MAG
+ ao_log_data.u.sensor.mag_along = ao_data_mag_along(d);
+ ao_log_data.u.sensor.mag_across = ao_data_mag_across(d);
+ ao_log_data.u.sensor.mag_through = ao_data_mag_through(d);
+# endif
+#else /* AO_LOG_NORMALIZED */
+#if HAS_MPU6000
ao_log_data.u.sensor.accel_x = d->mpu6000.accel_x;
ao_log_data.u.sensor.accel_y = d->mpu6000.accel_y;
ao_log_data.u.sensor.accel_z = d->mpu6000.accel_z;
ao_log_data.u.sensor.gyro_y = d->mpu6000.gyro_y;
ao_log_data.u.sensor.gyro_z = d->mpu6000.gyro_z;
#endif
-#endif
#if HAS_HMC5883
ao_log_data.u.sensor.mag_x = d->hmc5883.x;
ao_log_data.u.sensor.mag_z = d->hmc5883.z;
ao_log_data.u.sensor.mag_y = d->hmc5883.y;
#endif
-#ifdef HAS_MMC5983
- ao_log_data.u.sensor.mag_along = ao_data_mag_along(d);
- ao_log_data.u.sensor.mag_across = ao_data_mag_across(d);
- ao_log_data.u.sensor.mag_through = ao_data_mag_through(d);
-#endif
#if HAS_MPU9250
ao_log_data.u.sensor.accel_x = d->mpu9250.accel_x;
ao_log_data.u.sensor.accel_y = d->mpu9250.accel_y;
ao_log_data.u.sensor.mag_z = d->bmx160.mag_z;
ao_log_data.u.sensor.mag_y = d->bmx160.mag_y;
#endif
+#endif /* !AO_LOG_NORMALIZED */
ao_log_data.u.sensor.accel = ao_data_accel(&ao_data_ring[ao_log_data_pos]);
ao_log_write(&ao_log_data);
if (ao_log_state <= ao_flight_coast)
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef AO_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");
+}
}
static void
-ao_report_number(ao_v_t n)
+ao_report_number(int32_t n)
{
uint8_t digits[10];
uint8_t ndigits, i;
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
#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;
#include <ao_data.h>
#endif
+#ifndef HAS_KALMAN
+#define HAS_KALMAN 1
+#endif
+
#if HAS_GYRO
#include <ao_quaternion.h>
#endif
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
++nsamples;
else
ao_sample_preflight_set();
-#if !HAS_BARO
+#if !HAS_BARO && HAS_KALMAN
if ((nsamples & 0x3f) == 0)
ao_kalman_reset_accumulate();
#endif
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
#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;
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
#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
#if AO_LOG_NORMALIZED
#if AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_5
telemetry.generic.type = AO_TELEMETRY_MEGA_NORM_MPU6000_MMC5983;
+#elif AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_6
+ telemetry.generic.type = AO_TELEMETRY_MEGA_NORM_BMI088_MMC5983;
#else
#error unknown normalized log type
#endif
telemetry.mega_norm.pres = ao_data_pres(packet);
telemetry.mega_norm.temp = ao_data_temp(packet);
-#if HAS_MPU6000
+#ifdef ao_data_along
telemetry.mega_norm.accel_along = ao_data_along(packet);
telemetry.mega_norm.accel_across = ao_data_across(packet);
telemetry.mega_norm.accel_through = ao_data_through(packet);
telemetry.mega_norm.gyro_yaw = ao_data_yaw(packet);
#endif
-#if HAS_MMC5983
+#ifdef ao_data_mag_along
telemetry.mega_norm.mag_along = ao_data_mag_along(packet);
telemetry.mega_norm.mag_across = ao_data_mag_across(packet);
telemetry.mega_norm.mag_through = ao_data_mag_through(packet);
} 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);
}
};
#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 */
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_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_ */
--- /dev/null
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#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_ */
--- /dev/null
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#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;
+}
#
#
-include ../stmf0/Makefile.defs
+TOPDIR=..
+
+include $(TOPDIR)/stmf0/Makefile.defs
INC = \
ao.h \
ao_pins.h \
ao_product.h \
ao_task.h \
- ao_lisp.h \
- ao_lisp_const.h \
stm32f0.h \
Makefile
ao_romconfig.c \
ao_cmd.c \
ao_config.c \
+ ao_data.c \
ao_task.c \
ao_led_stmf0.c \
- ao_beep_stm.c \
ao_dma_stm.c \
ao_stdio.c \
ao_panic.c \
ao_timer.c \
ao_mutex.c \
ao_usb_stm.c \
- ao_serial_stm.c \
ao_flash_stm.c \
- ao_lisp_atom.c \
- ao_lisp_builtin.c \
- ao_lisp_cons.c \
- ao_lisp_error.c \
- ao_lisp_eval.c \
- ao_lisp_frame.c \
- ao_lisp_int.c \
- ao_lisp_lambda.c \
- ao_lisp_lex.c \
- ao_lisp_mem.c \
- ao_lisp_poly.c \
- ao_lisp_read.c \
- ao_lisp_rep.c \
- ao_lisp_save.c \
- ao_lisp_stack.c \
- ao_lisp_string.c \
- ao_lisp_os_save.c
+ ao_spi_stm.c \
+ ao_bmi088.c
PRODUCT=Nucleo-32
PRODUCT_DEF=-DNUCLEO
IDPRODUCT=0x000a
-CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS)
-
-LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stmf0 -Wl,-Tload.ld -n
+CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS) $(PROFILE_DEF)
PROGNAME=nucleo-32
PROG=$(PROGNAME)-$(VERSION).elf
all: $(PROG) $(HEX)
$(PROG): Makefile $(OBJ) altos.ld
- $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+ $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS) -Wl,-Map=$(PROGNAME)-$(VERSION).map
$(OBJ): $(INC)
-ao_product.h: ao-make-product.5c ../Version
- $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
-
-load: $(PROG)
- stm-load $(PROG)
-
distclean: clean
clean:
- rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
rm -f ao_product.h
install:
*/
#include <ao.h>
-#include <ao_lisp.h>
-#include <ao_beep.h>
+#include <ao_bmi088.h>
-static void lisp_cmd() {
- ao_lisp_read_eval_print();
-}
-
-static void beep() {
- ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
-}
-
-static const struct ao_cmds blink_cmds[] = {
- { lisp_cmd, "l\0Run lisp interpreter" },
- { beep, "b\0Beep" },
- { 0, 0 }
-};
+uint8_t ao_sensor_errors;
-void main(void)
+int main(void)
{
- ao_led_init(LEDS_AVAILABLE);
+ ao_led_init();
ao_clock_init();
ao_task_init();
ao_timer_init();
ao_dma_init();
ao_usb_init();
- ao_serial_init();
- ao_beep_init();
+ ao_spi_init();
+ ao_bmi088_init();
ao_cmd_init();
- ao_cmd_register(blink_cmds);
ao_start_scheduler();
}
#define HAS_USB 1
#define AO_USB_DIRECTIO 0
#define AO_PA11_PA12_RMP 0
-#define HAS_BEEP 1
-
-#define BEEPER_TIMER 2
-#define BEEPER_CHANNEL 4
-#define BEEPER_PORT (&stm_gpioa)
-#define BEEPER_PIN 3
+#define HAS_BEEP 0
#define IS_FLASH_LOADER 0
-#define HAS_SERIAL_2 1
+#define HAS_SERIAL_2 0
#define SERIAL_2_PA2_PA15 1
#define USE_SERIAL_2_FLOW 0
#define USE_SERIAL_2_STDIN 1
#define DELAY_SERIAL_2_STDIN 0
+#define HAS_SPI_1 1
+#define SPI_1_PA5_PA6_PA7 1
+#define SPI_1_OSPEEDR STM_OSPEEDR_HIGH
+#define SPI_1_PB3_PB4_PB5 0
+
+#define HAS_SPI_2 0
+
+#define HAS_BMI088 1
+#define HAS_IMU 1
+
+#define ao_data_along(packet) ((packet)->bmi088.acc.x)
+#define ao_data_across(packet) (-(packet)->bmi088.acc.y)
+#define ao_data_through(packet) ((packet)->bmi088.acc.z)
+
+#define ao_data_roll(packet) ((packet)->bmi088.gyr.x)
+#define ao_data_pitch(packet) (-(packet)->bmi088.gyr.y)
+#define ao_data_yaw(packet) ((packet)->bmi088.gyr.z)
+
+#define AO_BMI088_ACC_CS_PORT (&stm_gpioa)
+#define AO_BMI088_ACC_CS_PIN 0
+#define AO_BMI088_GYR_CS_PORT (&stm_gpioa)
+#define AO_BMI088_GYR_CS_PIN 1
+#define AO_BMI088_SPI_BUS (AO_SPI_1_PA5_PA6_PA7 | AO_SPI_MODE_0)
+
+#define AO_DATA_RING 32
+
#endif /* _AO_PINS_H_ */
--- /dev/null
+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:
--- /dev/null
+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)
--- /dev/null
+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
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+__flash = 0x00001000;
+__flash_size = 128K - 4K;
+__ram = 0x20000000;
+__ram_size = 16k;
+__stack_size = 256;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+__flash = 0x00001000;
+__flash_size = 256 - 4K;
+__ram = 0x20000000;
+__ram_size = 32k;
+__stack_size = 256;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
--- /dev/null
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+__flash = 0x00000000;
+__flash_size = 4K;
+__ram = 0x20000000;
+__ram_size = 4k;
+__stack_size = 256;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_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);
+}
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_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();
+}
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_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;
+}
--- /dev/null
+/*
+ * Copyright © 2020 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 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();
+}
--- /dev/null
+/*
+ * Copyright © 2020 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_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_ */
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_exti.h>
+#include <ao_boot.h>
+#include <ao_flash_task.h>
+
+int
+main(void)
+{
+ ao_clock_init();
+
+ ao_usb_init();
+
+#if HAS_TICK
+ ao_timer_init();
+#endif
+
+#ifdef AO_FLASH_LOADER_INIT
+ AO_FLASH_LOADER_INIT;
+#endif
+ ao_flash_task();
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 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();
+}
+
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_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_ */
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_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_ */
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#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
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_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
+}
--- /dev/null
+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;
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+#
+# 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:
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_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_ */
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=snekboard
+include $(TOPDIR)/samd21/Makefile-flash.defs
--- /dev/null
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#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_ */
--- /dev/null
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#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;
+}
void
ao_spi_put(uint8_t spi_index);
+void
+ao_spi_put_pins(uint8_t spi_index);
+
void
ao_spi_send(const void *block, uint16_t len, uint8_t spi_index);
ao_mutex_put(&ao_spi_mutex[id]);
}
+void
+ao_spi_put_pins(uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+
+ ao_spi_disable_pin_config(ao_spi_pin_config[id]);
+ ao_spi_pin_config[id] = AO_SPI_CONFIG_NONE;
+ ao_spi_put(spi_index);
+}
+
static void
ao_spi_channel_init(uint8_t spi_index)
{
void
ao_spi_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);
void
ao_spi_put(uint8_t spi_index);
+void
+ao_spi_put_pins(uint8_t spi_index);
+
void
ao_spi_send(const void *block, uint16_t len, uint8_t spi_index);
ao_mutex_put(&ao_spi_mutex[id]);
}
+void
+ao_spi_put_pins(uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+
+ ao_spi_disable_pin_config(ao_spi_pin_config[id]);
+ ao_spi_pin_config[id] = AO_SPI_CONFIG_NONE;
+ ao_spi_put(spi_index);
+}
+
static void
ao_spi_channel_init(uint8_t spi_index)
{
#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
#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
/* UI values */
static uint8_t ao_lco_select_mode;
+static uint8_t ao_lco_event_debug;
+
+#define PRINTE(...) do { if (!ao_lco_debug && !ao_lco_event_debug) break; printf ("\r%5lu %s: ", (unsigned long) ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
#define AO_LCO_SELECT_PAD 0
#define AO_LCO_SELECT_BOX 1
for (;;) {
ao_event_get(&event);
- PRINTD("event type %d unit %d value %ld\n",
+ PRINTE("event type %d unit %d value %ld\n",
event.type, event.unit, (long) event.value);
switch (event.type) {
case AO_EVENT_QUADRATURE:
ao_lco_set_debug(void)
{
uint32_t r = ao_cmd_decimal();
- if (ao_cmd_status == ao_cmd_success)
- ao_lco_debug = r != 0;
+ if (ao_cmd_status == ao_cmd_success){
+ ao_lco_debug = r & 1;
+ ao_lco_event_debug = (r & 2) >> 1;
+ }
}
const struct ao_cmds ao_lco_cmds[] = {
{ ao_lco_set_debug, "D <0 off, 1 on>\0Debug" },
{ ao_lco_search, "s\0Search for pad boxes" },
+ { ao_lco_pretend, "p\0Pretend there are lots of pad boxes" },
{ 0, NULL }
};
#endif
--- /dev/null
+#
+# 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:
--- /dev/null
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+
+/* 16MHz High speed external crystal */
+#define AO_HSE 16000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL 6
+#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_6)
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV 3
+#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER 2
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER 2
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_SERIAL_1 0
+#define USE_SERIAL_1_STDIN 0
+#define SERIAL_1_PB6_PB7 0
+#define SERIAL_1_PA9_PA10 0
+
+#define HAS_SERIAL_2 1
+#define USE_SERIAL_2_STDIN 0
+#define SERIAL_2_PA2_PA3 1
+#define SERIAL_2_PD5_PD6 0
+#define USE_SERIAL_2_FLOW 0
+#define USE_SERIAL_2_SW_FLOW 0
+
+#define HAS_SERIAL_3 0
+#define USE_SERIAL_3_STDIN 0
+#define SERIAL_3_PB10_PB11 0
+#define SERIAL_3_PC10_PC11 0
+#define SERIAL_3_PD8_PD9 0
+
+#define ao_gps_getchar ao_serial2_getchar
+#define ao_gps_putchar ao_serial2_putchar
+#define ao_gps_set_speed ao_serial2_set_speed
+#define ao_gps_fifo (ao_stm_usart2.rx_fifo)
+
+#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX (1024 * 1024)
+#define AO_CONFIG_MAX_SIZE 1024
+#define LOG_ERASE_MARK 0x55
+#define LOG_MAX_ERASE 128
+#define AO_LOG_FORMAT AO_LOG_FORMAT_TELEMEGA_6
+#define AO_LOG_NORMALIZED 1
+
+#define HAS_EEPROM 1
+#define USE_INTERNAL_FLASH 0
+#define USE_EEPROM_CONFIG 1
+#define USE_STORAGE_CONFIG 0
+#define HAS_USB 1
+#define HAS_BEEP 1
+#define BEEPER_TIMER 3
+#define BEEPER_CHANNEL 2
+#define BEEPER_PORT (&stm_gpioe)
+#define BEEPER_PIN 4
+#define AO_BEEP_MID_DEFAULT 179 /* 2100 Hz */
+#define AO_BEEP_MAKE_LOW(m) ((uint8_t) ((m) * 197U/179U)) /* 1900 Hz */
+#define AO_BEEP_MAKE_HIGH(m) ((uint8_t) ((m) * 163U/179U)) /* 2300 Hz */
+#define HAS_BATTERY_REPORT 1
+#define HAS_RADIO 1
+#define HAS_TELEMETRY 1
+#define HAS_APRS 1
+#define HAS_COMPANION 1
+
+#define HAS_SPI_1 1
+#define SPI_1_PA5_PA6_PA7 1 /* Barometer */
+#define SPI_1_PB3_PB4_PB5 1 /* Accelerometer */
+#define SPI_1_PE13_PE14_PE15 1 /* MPU6000 */
+#define SPI_1_OSPEEDR STM_OSPEEDR_10MHz
+
+//#define MMC5983_I2C 1
+
+#define HAS_SPI_2 1
+#define SPI_2_PB13_PB14_PB15 1 /* Flash, Companion */
+#ifndef MMC5983_I2C
+#define SPI_2_PD1_PD3_PD4 1 /* MMC5983 */
+#endif
+#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz
+
+#define HAS_I2C_1 0
+#define I2C_1_PB8_PB9 0
+
+#define HAS_I2C_2 0
+#define I2C_2_PB10_PB11 0
+
+#define PACKET_HAS_SLAVE 1
+#define PACKET_HAS_MASTER 0
+
+#define LOW_LEVEL_DEBUG 0
+
+#define LED_PORT_ENABLE STM_RCC_AHBENR_GPIOCEN
+#define LED_PORT (&stm_gpioc)
+#define LED_PIN_RED 8
+#define LED_PIN_GREEN 9
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_GPS 1
+#define HAS_FLIGHT 1
+#define HAS_ADC 1
+#define HAS_ADC_TEMP 1
+#define HAS_LOG 1
+
+/*
+ * Igniter
+ */
+
+#define HAS_IGNITE 1
+#define HAS_IGNITE_REPORT 1
+
+#define AO_SENSE_PYRO(p,n) ((p)->adc.sense[n])
+#define AO_SENSE_DROGUE(p) ((p)->adc.sense[4])
+#define AO_SENSE_MAIN(p) ((p)->adc.sense[5])
+#define AO_IGNITER_CLOSED 400
+#define AO_IGNITER_OPEN 60
+
+/* Pyro A */
+#define AO_PYRO_PORT_0 (&stm_gpiod)
+#define AO_PYRO_PIN_0 6
+
+/* Pyro B */
+#define AO_PYRO_PORT_1 (&stm_gpiod)
+#define AO_PYRO_PIN_1 7
+
+/* Pyro C */
+#define AO_PYRO_PORT_2 (&stm_gpioe)
+#define AO_PYRO_PIN_2 3
+
+/* Pyro D */
+#define AO_PYRO_PORT_3 (&stm_gpioe)
+#define AO_PYRO_PIN_3 2
+
+/* Drogue */
+#define AO_IGNITER_DROGUE_PORT (&stm_gpioe)
+#define AO_IGNITER_DROGUE_PIN 6
+
+/* Main */
+#define AO_IGNITER_MAIN_PORT (&stm_gpioe)
+#define AO_IGNITER_MAIN_PIN 5
+
+/* Number of general purpose pyro channels available */
+#define AO_PYRO_NUM 4
+
+/*
+ * ADC
+ */
+#define AO_DATA_RING 32
+#define AO_ADC_NUM_SENSE 6
+
+struct ao_adc {
+ int16_t sense[AO_ADC_NUM_SENSE];
+ int16_t v_batt;
+ int16_t v_pbatt;
+ int16_t temp;
+};
+
+#define AO_ADC_DUMP(p) \
+ printf("tick: %5lu A: %5d B: %5d C: %5d D: %5d drogue: %5d main: %5d batt: %5d pbatt: %5d temp: %5d\n", \
+ (p)->tick, \
+ (p)->adc.sense[0], (p)->adc.sense[1], (p)->adc.sense[2], \
+ (p)->adc.sense[3], (p)->adc.sense[4], (p)->adc.sense[5], \
+ (p)->adc.v_batt, (p)->adc.v_pbatt, (p)->adc.temp)
+
+#define AO_ADC_SENSE_A 0
+#define AO_ADC_SENSE_A_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_A_PIN 0
+
+#define AO_ADC_SENSE_B 1
+#define AO_ADC_SENSE_B_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_B_PIN 1
+
+#define AO_ADC_SENSE_C 24
+#define AO_ADC_SENSE_C_PORT (&stm_gpioe)
+#define AO_ADC_SENSE_C_PIN 9
+
+#define AO_ADC_SENSE_D 25
+#define AO_ADC_SENSE_D_PORT (&stm_gpioe)
+#define AO_ADC_SENSE_D_PIN 10
+
+#define AO_ADC_SENSE_DROGUE 4
+#define AO_ADC_SENSE_DROGUE_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_DROGUE_PIN 4
+
+#define AO_ADC_SENSE_MAIN 22
+#define AO_ADC_SENSE_MAIN_PORT (&stm_gpioe)
+#define AO_ADC_SENSE_MAIN_PIN 7
+
+#define AO_ADC_V_BATT 8
+#define AO_ADC_V_BATT_PORT (&stm_gpiob)
+#define AO_ADC_V_BATT_PIN 0
+
+#define AO_ADC_V_PBATT 9
+#define AO_ADC_V_PBATT_PORT (&stm_gpiob)
+#define AO_ADC_V_PBATT_PIN 1
+
+#define AO_ADC_TEMP 16
+
+#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_GPIOAEN) | \
+ (1 << STM_RCC_AHBENR_GPIOEEN) | \
+ (1 << STM_RCC_AHBENR_GPIOBEN))
+
+#define AO_NUM_ADC_PIN (AO_ADC_NUM_SENSE + 2)
+
+#define AO_ADC_PIN0_PORT AO_ADC_SENSE_A_PORT
+#define AO_ADC_PIN0_PIN AO_ADC_SENSE_A_PIN
+#define AO_ADC_PIN1_PORT AO_ADC_SENSE_B_PORT
+#define AO_ADC_PIN1_PIN AO_ADC_SENSE_B_PIN
+#define AO_ADC_PIN2_PORT AO_ADC_SENSE_C_PORT
+#define AO_ADC_PIN2_PIN AO_ADC_SENSE_C_PIN
+#define AO_ADC_PIN3_PORT AO_ADC_SENSE_D_PORT
+#define AO_ADC_PIN3_PIN AO_ADC_SENSE_D_PIN
+#define AO_ADC_PIN4_PORT AO_ADC_SENSE_DROGUE_PORT
+#define AO_ADC_PIN4_PIN AO_ADC_SENSE_DROGUE_PIN
+#define AO_ADC_PIN5_PORT AO_ADC_SENSE_MAIN_PORT
+#define AO_ADC_PIN5_PIN AO_ADC_SENSE_MAIN_PIN
+#define AO_ADC_PIN6_PORT AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN6_PIN AO_ADC_V_BATT_PIN
+#define AO_ADC_PIN7_PORT AO_ADC_V_PBATT_PORT
+#define AO_ADC_PIN7_PIN AO_ADC_V_PBATT_PIN
+
+#define AO_NUM_ADC (AO_ADC_NUM_SENSE + 3)
+
+#define AO_ADC_SQ1 AO_ADC_SENSE_A
+#define AO_ADC_SQ2 AO_ADC_SENSE_B
+#define AO_ADC_SQ3 AO_ADC_SENSE_C
+#define AO_ADC_SQ4 AO_ADC_SENSE_D
+#define AO_ADC_SQ5 AO_ADC_SENSE_DROGUE
+#define AO_ADC_SQ6 AO_ADC_SENSE_MAIN
+#define AO_ADC_SQ7 AO_ADC_V_BATT
+#define AO_ADC_SQ8 AO_ADC_V_PBATT
+#define AO_ADC_SQ9 AO_ADC_TEMP
+
+/*
+ * Voltage divider on ADC battery sampler
+ */
+#define AO_BATTERY_DIV_PLUS 56 /* 5.6k */
+#define AO_BATTERY_DIV_MINUS 100 /* 10k */
+
+/*
+ * Voltage divider on ADC igniter samplers
+ */
+#define AO_IGNITE_DIV_PLUS 100 /* 100k */
+#define AO_IGNITE_DIV_MINUS 27 /* 27k */
+
+/*
+ * ADC reference in decivolts
+ */
+#define AO_ADC_REFERENCE_DV 33
+
+/*
+ * Pressure sensor settings
+ */
+#define HAS_MS5607 1
+#define HAS_MS5611 0
+#define AO_MS5607_PRIVATE_PINS 1
+#define AO_MS5607_CS_PORT (&stm_gpioc)
+#define AO_MS5607_CS_PIN 4
+#define AO_MS5607_CS_MASK (1 << AO_MS5607_CS)
+#define AO_MS5607_MISO_PORT (&stm_gpioa)
+#define AO_MS5607_MISO_PIN 6
+#define AO_MS5607_MISO_MASK (1 << AO_MS5607_MISO)
+#define AO_MS5607_SPI_INDEX AO_SPI_1_PA5_PA6_PA7
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS 1
+#define AO_M25_SPI_CS_PORT (&stm_gpiod)
+#define AO_M25_SPI_CS_MASK (1 << 10)
+#define AO_M25_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Radio (cc1200)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT 5695733
+
+#define AO_FEC_DEBUG 0
+#define AO_CC1200_SPI_CS_PORT (&stm_gpioc)
+#define AO_CC1200_SPI_CS_PIN 5
+#define AO_CC1200_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+#define AO_CC1200_SPI stm_spi2
+
+#define AO_CC1200_INT_PORT (&stm_gpiob)
+#define AO_CC1200_INT_PIN 11
+
+#define AO_CC1200_INT_GPIO 2
+#define AO_CC1200_INT_GPIO_IOCFG CC1200_IOCFG2
+
+#define AO_CC1200_MARC_GPIO 3
+#define AO_CC1200_MARC_GPIO_IOCFG CC1200_IOCFG3
+
+#define HAS_BOOT_RADIO 0
+
+
+/*
+ *
+ * If the board is laying component side up with
+ * the antenna (nose) pointing north
+ *
+ * +along north +roll left up
+ * +across west +pitch nose down
+ * +through up +yaw left turn
+ */
+
+/*
+ * bmi088
+ *
+ * pin 1 NE corner of chip
+ *
+ * +along +Y +roll +Y
+ * +across -X +pitch -X
+ * +through +Z +yaw +Z
+ */
+
+#define HAS_BMI088 1
+#define AO_BMI088_SPI_BUS AO_SPI_1_PE13_PE14_PE15
+#define AO_BMI088_ACC_CS_PORT (&stm_gpioc)
+#define AO_BMI088_ACC_CS_PIN 14
+#define AO_BMI088_GYR_CS_PORT (&stm_gpioc)
+#define AO_BMI088_GYR_CS_PIN 13
+#define HAS_IMU 1
+
+#define ao_bmi088_along(m) ((m)->acc.y)
+#define ao_bmi088_across(m) (-(m)->acc.x)
+#define ao_bmi088_through(m) ((m)->acc.z)
+
+#define ao_bmi088_roll(m) ((m)->gyr.y)
+#define ao_bmi088_pitch(m) (-(m)->gyr.x)
+#define ao_bmi088_yaw(m) ((m)->gyr.z)
+
+#define ao_data_along(packet) ao_bmi088_along(&(packet)->bmi088)
+#define ao_data_across(packet) ao_bmi088_across(&(packet)->bmi088)
+#define ao_data_through(packet) ao_bmi088_through(&(packet)->bmi088)
+
+#define ao_data_roll(packet) ao_bmi088_roll(&(packet)->bmi088)
+#define ao_data_pitch(packet) ao_bmi088_pitch(&(packet)->bmi088)
+#define ao_data_yaw(packet) ao_bmi088_yaw(&(packet)->bmi088)
+
+/*
+ * MMC5983
+ *
+ * pin 1 NE corner of chip
+ *
+ * +along -Y
+ * +across +X
+ * +through -Z
+ */
+
+#define HAS_MMC5983 1
+#define AO_MMC5983_INT_PORT (&stm_gpiod)
+#define AO_MMC5983_INT_PIN 5
+#define AO_MMC5983_SPI_CLK_PORT (&stm_gpiod)
+#define AO_MMC5983_SPI_CLK_PIN 1
+#define AO_MMC5983_SPI_MISO_PORT (&stm_gpiod)
+#define AO_MMC5983_SPI_MISO_PIN 3
+#define AO_MMC5983_SPI_MOSI_PORT (&stm_gpiod)
+#define AO_MMC5983_SPI_MOSI_PIN 4
+#define AO_MMC5983_SPI_INDEX (AO_SPI_2_PD1_PD3_PD4 | AO_SPI_MODE_3)
+#define AO_MMC5983_SPI_CS_PORT (&stm_gpioa)
+#define AO_MMC5983_SPI_CS_PIN 15
+
+#define ao_mmc5983_along(m) (-(m)->y)
+#define ao_mmc5983_across(m) ((m)->x)
+#define ao_mmc5983_through(m) (-(m)->z)
+
+#define ao_data_mag_along(packet) ao_mmc5983_along(&(packet)->mmc5983)
+#define ao_data_mag_across(packet) ao_mmc5983_across(&(packet)->mmc5983)
+#define ao_data_mag_through(packet) ao_mmc5983_through(&(packet)->mmc5983)
+
+/*
+ * ADXL375
+ *
+ * pin 1 NW corner of chip
+ *
+ * +along +X
+ * +across +Y
+ * +through +Z
+ */
+
+#define HAS_ADXL375 1
+#define AO_ADXL375_SPI_INDEX (AO_SPI_1_PB3_PB4_PB5 | AO_SPI_MODE_3)
+#define AO_ADXL375_CS_PORT (&stm_gpioc)
+#define AO_ADXL375_CS_PIN 0
+
+#define AO_ADXL375_AXIS x
+#define AO_ADXL375_INVERT 1
+
+#define NUM_CMDS 16
+
+/*
+ * Companion
+ */
+
+#define AO_COMPANION_CS_PORT (&stm_gpiob)
+#define AO_COMPANION_CS_PIN_0 (6)
+#define AO_COMPANION_CS_PIN AO_COMPANION_CS_PIN_0
+#define AO_COMPANION_CS_PIN_1 (7)
+#define AO_COMPANION_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Monitor
+ */
+
+#define HAS_MONITOR 0
+#define LEGACY_MONITOR 0
+#define HAS_MONITOR_PUT 1
+#define AO_MONITOR_LED 0
+#define HAS_RSSI 0
+
+/*
+ * Profiling Viterbi decoding
+ */
+
+#ifndef AO_PROFILE
+#define AO_PROFILE 0
+#endif
+
+/*
+ * PWM output
+ */
+
+#define NUM_PWM 4
+#define PWM_MAX 20000
+#define AO_PWM_TIMER stm_tim4
+#define AO_PWM_TIMER_ENABLE STM_RCC_APB1ENR_TIM4EN
+#define AO_PWM_TIMER_SCALE 32
+
+#define AO_PWM_0_GPIO (&stm_gpiod)
+#define AO_PWM_0_PIN 12
+
+#define AO_PWM_1_GPIO (&stm_gpiod)
+#define AO_PWM_1_PIN 13
+
+#define AO_PWM_2_GPIO (&stm_gpiod)
+#define AO_PWM_2_PIN 15
+
+#define AO_PWM_3_GPIO (&stm_gpiod)
+#define AO_PWM_3_PIN 14
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+/*
+ * Copyright © 2021 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_bmi088.h>
+#include <ao_mmc5983.h>
+#include <ao_adxl375.h>
+#include <ao_log.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_profile.h>
+#include <ao_eeprom.h>
+#include <ao_i2c_bit.h>
+#if HAS_SAMPLE_PROFILE
+#include <ao_sample_profile.h>
+#endif
+#include <ao_pyro.h>
+#if HAS_STACK_GUARD
+#include <ao_mpu.h>
+#endif
+#include <ao_pwm.h>
+
+int
+main(void)
+{
+ ao_clock_init();
+
+#if HAS_STACK_GUARD
+ ao_mpu_init();
+#endif
+
+ ao_task_init();
+ ao_serial_init();
+ ao_led_init();
+ ao_led_on(LEDS_AVAILABLE);
+ ao_timer_init();
+
+ ao_spi_init();
+#ifdef MMC5983_I2C
+ ao_i2c_bit_init();
+#endif
+ ao_dma_init();
+ ao_exti_init();
+
+ ao_adc_init();
+#if HAS_BEEP
+ ao_beep_init();
+#endif
+ ao_cmd_init();
+
+ ao_ms5607_init();
+ ao_bmi088_init();
+ ao_mmc5983_init();
+ ao_adxl375_init();
+
+ ao_eeprom_init();
+ ao_storage_init();
+
+ ao_flight_init();
+ ao_log_init();
+ ao_report_init();
+
+ ao_usb_init();
+ ao_gps_init();
+ ao_gps_report_mega_init();
+ ao_telemetry_init();
+ ao_radio_init();
+ ao_packet_slave_init(false);
+ ao_igniter_init();
+ ao_companion_init();
+ ao_pyro_init();
+
+ ao_config_init();
+#if AO_PROFILE
+ ao_profile_init();
+#endif
+#if HAS_SAMPLE_PROFILE
+ ao_sample_profile_init();
+#endif
+
+ ao_pwm_init();
+
+ ao_led_off(LEDS_AVAILABLE);
+
+ ao_start_scheduler();
+ return 0;
+}
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telemega-v6.0
+include $(TOPDIR)/stm/Makefile-flash.defs
--- /dev/null
+/*
+ * Copyright © 2021 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* External crystal at 16MHz */
+#define AO_HSE 16000000
+
+#include <ao_flash_stm_pins.h>
+
+/* Companion port cs_companion0 PB6 */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpiob
+#define AO_BOOT_APPLICATION_PIN 6
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+#
+# AltOS build
+#
+#
+
+include ../samd21/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_boot.h \
+ ao_companion.h \
+ ao_data.h \
+ ao_sample.h \
+ ao_pins.h \
+ altitude-pa.h \
+ ao_kalman.h \
+ ao_product.h \
+ ao_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:
--- /dev/null
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_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_ */
--- /dev/null
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_ms5607.h>
+#include <ao_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;
+}
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telemetrum-v4.0
+include $(TOPDIR)/samd21/Makefile-flash.defs
--- /dev/null
+/*
+ * 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_ */
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;
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;
"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;
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");