no longer need patch debian debian/1.9.18-1
authorBdale Garbee <bdale@gag.com>
Mon, 29 Apr 2024 01:45:53 +0000 (19:45 -0600)
committerBdale Garbee <bdale@gag.com>
Mon, 29 Apr 2024 01:45:53 +0000 (19:45 -0600)
150 files changed:
ChangeLog
Makefile.am
Releasing
altoslib/AltosConfigData.java
altoslib/AltosConfigValues.java
altoslib/AltosConvert.java
altoslib/AltosEepromRecordSet.java
altoslib/AltosEepromRecordTimer.java [new file with mode: 0644]
altoslib/AltosIMU.java
altoslib/AltosIdleFetch.java
altoslib/AltosLib.java
altoslib/AltosMag.java
altoslib/AltosSensorEasyTimer2.java [new file with mode: 0644]
altoslib/Makefile.am
altosui/AltosConfigFCUI.java
altosui/Makefile.am
altosui/altos-windows.nsi.in
altosuilib/AltosUIAccelCal.java
ao-tools/ao-flash/Makefile.am
ao-tools/ao-flash/ao-flash-stm32f1 [new file with mode: 0755]
ao-tools/ao-flash/ao-flash-stm32f1.1 [new file with mode: 0644]
configure.ac
debian/changelog
debian/patches/fix-typo [deleted file]
debian/patches/series
debian/rules
doc/Makefile.am
doc/altusmetrum-theme.yml
doc/altusmetrum.txt
doc/easymini-release-notes.inc
doc/header.inc
doc/motortest-installation.inc
doc/release-notes-1.9.18.inc [new file with mode: 0644]
doc/release-notes.inc
doc/specs.inc
doc/telegps-release-notes.inc
micropeak/Makefile.am
micropeak/micropeak-windows.nsi.in
src/Makefile
src/draw/Makefile
src/draw/ao_draw.h
src/draw/ao_line.c
src/draw/ao_logo.c
src/draw/ao_text.c
src/draw/ao_text_width.c [new file with mode: 0644]
src/draw/font-convert
src/drivers/ao_adxl375.c
src/drivers/ao_bmi088.c
src/drivers/ao_bmx160.c
src/drivers/ao_cc1120.c
src/drivers/ao_cc115l.c
src/drivers/ao_cc1200.c
src/drivers/ao_companion.c
src/drivers/ao_lco.c
src/drivers/ao_lco.h
src/drivers/ao_lco_bits.c
src/drivers/ao_lco_two.c
src/drivers/ao_m25.c
src/drivers/ao_mma655x.c
src/drivers/ao_mmc5983.c
src/drivers/ao_mpu6000.c
src/drivers/ao_mpu9250.c
src/drivers/ao_ms5607.c
src/drivers/ao_seven_segment.c
src/drivers/ao_st7565.c [new file with mode: 0644]
src/drivers/ao_st7565.h [new file with mode: 0644]
src/easymega-v3.0/Makefile
src/easymega-v3.0/ao_easymega.c
src/easymega-v3.0/ao_pins.h
src/easymega-v3.0/flash-loader/Makefile
src/easymega-v3.0/flash-loader/ao_pins.h
src/easymini-v2.0/Makefile
src/easytimer-v2/.gitignore [new file with mode: 0644]
src/easytimer-v2/Makefile [new file with mode: 0644]
src/easytimer-v2/ao_easytimer.c [new file with mode: 0644]
src/easytimer-v2/ao_pins.h [new file with mode: 0644]
src/easytimer-v2/flash-loader/Makefile [new file with mode: 0644]
src/easytimer-v2/flash-loader/ao_pins.h [new file with mode: 0644]
src/kernel/ao_adc_single.h [new file with mode: 0644]
src/kernel/ao_config.c
src/kernel/ao_data.h
src/kernel/ao_fec_tx.c
src/kernel/ao_log.h
src/kernel/ao_log_timer.c [new file with mode: 0644]
src/kernel/ao_pyro.c
src/lpc/ao_adc_lpc.c
src/lpc/ao_arch.h
src/samd21/ao_arch_funcs.h
src/samd21/ao_usb_samd21.c
src/snekboard/snekboard.c
src/stm/ao_adc_single.h [deleted file]
src/stm/ao_arch_funcs.h
src/stm32f1/Makefile-flash.defs [new file with mode: 0644]
src/stm32f1/Makefile-raw.defs [new file with mode: 0644]
src/stm32f1/Makefile-stm32f1.defs [new file with mode: 0644]
src/stm32f1/Makefile.defs [new file with mode: 0644]
src/stm32f1/altos-128.ld [new file with mode: 0644]
src/stm32f1/altos-loader.ld [new file with mode: 0644]
src/stm32f1/altos-ram.ld [new file with mode: 0644]
src/stm32f1/altos-raw.ld [new file with mode: 0644]
src/stm32f1/altos.ld [new file with mode: 0644]
src/stm32f1/ao_adc_single_stm.c [new file with mode: 0644]
src/stm32f1/ao_adc_stm.c [new file with mode: 0644]
src/stm32f1/ao_arch.h [new file with mode: 0644]
src/stm32f1/ao_arch_funcs.h [new file with mode: 0644]
src/stm32f1/ao_beep_stm.c [new file with mode: 0644]
src/stm32f1/ao_boot_chain.c [new file with mode: 0644]
src/stm32f1/ao_boot_pin.c [new file with mode: 0644]
src/stm32f1/ao_clock.c [new file with mode: 0644]
src/stm32f1/ao_dma_stm.c [new file with mode: 0644]
src/stm32f1/ao_eeprom.c [new file with mode: 0644]
src/stm32f1/ao_exti.h [new file with mode: 0644]
src/stm32f1/ao_exti_stm.c [new file with mode: 0644]
src/stm32f1/ao_fast_timer.c [new file with mode: 0644]
src/stm32f1/ao_flash.h [new file with mode: 0644]
src/stm32f1/ao_flash_loader_stm.c [new file with mode: 0644]
src/stm32f1/ao_flash_stm.c [new file with mode: 0644]
src/stm32f1/ao_flash_stm_pins.h [new file with mode: 0644]
src/stm32f1/ao_i2c_stm.c [new file with mode: 0644]
src/stm32f1/ao_interrupt.c [new file with mode: 0644]
src/stm32f1/ao_pwm_stm.c [new file with mode: 0644]
src/stm32f1/ao_serial_stm.c [new file with mode: 0644]
src/stm32f1/ao_spi_stm.c [new file with mode: 0644]
src/stm32f1/ao_timer.c [new file with mode: 0644]
src/stm32f1/ao_usb_stm.c [new file with mode: 0644]
src/stm32f1/openocd-stm32f1 [new file with mode: 0755]
src/stm32f1/registers.ld [new file with mode: 0644]
src/stm32f1/stm32f1.h [new file with mode: 0644]
src/stm32f103-nucleo/.gitignore [new file with mode: 0644]
src/stm32f103-nucleo/Makefile [new file with mode: 0644]
src/stm32f103-nucleo/ao_pins.h [new file with mode: 0644]
src/stm32f103-nucleo/flash-loader/Makefile [new file with mode: 0644]
src/stm32f103-nucleo/flash-loader/ao_pins.h [new file with mode: 0644]
src/stm32f103-nucleo/hello.c [new file with mode: 0644]
src/stm32f103-nucleo/hello.ld [new file with mode: 0644]
src/stm32l0/ao_arch_funcs.h
src/stmf0/ao_arch_funcs.h
src/telelco-v0.2-cc1200/ao_pins.h
src/telelco-v0.2/ao_pins.h
src/telelco-v0.3/ao_pins.h
src/telelco-v2.0/ao_lco_v2.c
src/telelco-v3.0/.gitignore [new file with mode: 0644]
src/telelco-v3.0/Makefile [new file with mode: 0644]
src/telelco-v3.0/ao_lco_v3.c [new file with mode: 0644]
src/telelco-v3.0/ao_pins.h [new file with mode: 0644]
src/telelco-v3.0/ao_telelco.c [new file with mode: 0644]
src/telelco-v3.0/flash-loader/Makefile [new file with mode: 0644]
src/telelco-v3.0/flash-loader/ao_pins.h [new file with mode: 0644]
src/test/ao_fec_test.c
telegps/TeleGPSConfigUI.java

index a7983f83e219840809644ec94580ce67a334d366..eaa18865d3fe1510b8660eaa57765d1b9987af47 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
+commit d8a81e14d572c4fa8d1b5aa7cb477654c206ae1d
+Merge: 1311eba0 dce00ba4
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Apr 28 19:40:16 2024 -0600
+
+    Merge branch 'master' into branch-1.9
+
+commit dce00ba450e686e0fef4bd1d6a8b292eca8bfc25
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Apr 28 15:34:13 2024 -0700
+
+    Version 1.9.18
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b834c989cbddf8db9848af03bb040da1a6a0f651
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 27 22:30:29 2024 -0700
+
+    doc: Add 1.9.18 release notes
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ae69294526369f251b221ee1f8ca2cb58b63bd7a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Apr 28 18:34:30 2024 -0700
+
+    altos/draw: Add 'install' target
+    
+    Needed for building packages; install is a no-op in this directory.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1311eba0bbe32bc1759a3b11b00c78774843383c
+Merge: 7b2588ee a0005e3f
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Apr 28 18:44:32 2024 -0600
+
+    Merge branch 'master' into branch-1.9
+
+commit f89bfb0e1a14c9809d35f60fff448a1b3a2f2b68
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Apr 28 15:49:29 2024 -0700
+
+    Fix up fat build target
+    
+    Depend on all-recursive so everything gets built before we attempt to
+    build the fat bits.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a0005e3f0cc65f874b112996899a782393a1c12d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Apr 28 15:34:13 2024 -0700
+
+    Version 1.9.18
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f5c037001c51609608940cf16e9e99740aad6ebc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 27 22:30:29 2024 -0700
+
+    doc: Add 1.9.18 release notes
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f8d943c54b1518e77c884b6dc77ee42dc88aaeab
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Apr 28 15:33:30 2024 -0700
+
+    Add EasyTimer v2 firmware
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 278f686f014e74962721e47aeeb60b59afafb37d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 3 12:31:54 2024 -0800
+
+    altosui: Support gps receiver setting
+    
+    Create a combo box listing the available receiver models and allow the
+    user to select which one to use, including the builtin one.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5c815c673fa7e82419299d87ffde939fbd842333
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 3 12:24:08 2024 -0800
+
+    altosui: Set the beeper to 0 to disable
+    
+    Don't force the beeper to the default value when the frequency is set to 0.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2bd6698fb9d403931568ddc80266d1bb61c09a8b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 19 14:07:50 2024 -0700
+
+    altos/test: Add FEC test for simple 'hello' message
+    
+    This just tests the FEC code using a slightly different data source
+    that makes sure odd lengths work correctly.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 19c42c4d97151f2dc38e59cff4d0638694fcd27d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 28 09:45:38 2024 -0700
+
+    altos/test: Adjust CRC error rate after FEC fix
+    
+    The number of CRC failures during FEC testing is lower now that
+    the packet length bug has been fixed in the FEC encode bits.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit abfa580ad700415f5ea240450a1621f9de35de82
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 26 21:38:48 2024 -0700
+
+    altos: ao_fec_prepare using wrong value for input len
+    
+    The FEC code always sends a multiple of four bytes, padding by two
+    bytes for even inputs and one byte for odd. But the preparation step
+    was using the wrong value for the length, so the output was getting
+    mangled.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d7a91278051ff75e3edc3e999a6d8096fa4deec4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 3 16:37:09 2024 -0800
+
+    altos/easytimer-v2: Generate combined .ihx file for seeed testing
+    
+    The test plan for seeed requires a combined loader and firmware image
+    for a single flashing step.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a017cfce31429ef25a53a724e99b53c8267ec4ef
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 2 16:23:57 2024 -0700
+
+    altos/stm32f1: Grab both TX/RX DMA mutexes while doing I2C
+    
+    The I2C engine appears to trigger an extra RX DMA transaction which
+    scrambles anything sharing the same DMA units.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d9140ea0d6bf6755a2701115273f02a4c3944e17
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 24 12:46:17 2024 -0800
+
+    altos/easymega-v3.0: Update i2c pin usage
+    
+    Found an errata which means we had to swap which i2c to use with the
+    mag sensor. Swap the mag sensor and fire_c/fire_d.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bb3bfea5462d3f3e1a0a5f4645f67996054a78a3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 24 12:45:54 2024 -0800
+
+    altos: Build EasyMega v3.0 by defaul
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 22b5d1a98a86ac4d2d0ef12565a01e3591f9afb9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 24 12:45:09 2024 -0800
+
+    altos/stm32f103: Fix continuous ADC code
+    
+    This needs the same hacks as the manual ADC code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6a9721f344d1837c913fee142c875de4d7357820
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 24 12:44:00 2024 -0800
+
+    altoslib: Add EasyMega v3.0 support
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4da8e047c4df06a0fec2c0cd47d26d5f1bef0e31
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Feb 12 22:58:45 2024 -0800
+
+    src/easymini-2.0: Add combined .dfu file for Seeed testing
+    
+    Provide a combined .dfu for testing EasyMini at Seeed. This
+    checks the baro sensor (via POST) and SoC.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 94445131d0be72f099985709dda4203259b1d947
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Feb 2 16:31:03 2024 -0800
+
+    stm32f1: Clean up some ADC definitions
+    
+    The CR2 bit is called TSVREFE in the docs, use that consistently
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bcf9db74705dcd77eccd8f36f73c2eb99a5f1c70
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 31 22:05:13 2024 -0800
+
+    altosui: Make accel cal dialog say 'beeper' instead of 'antenna'
+    
+    Quite the plumbing required, but it'll do.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 13befe46b107a88d05710991beab3b7b8478fa09
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 29 18:40:30 2024 -0800
+
+    altos/easytimer-v2: Fix product name
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9419cf4f22e5cd337da54d7907fc0a5e848bd464
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 29 18:40:06 2024 -0800
+
+    altos/easytimer-v2: Set default log size to 192kB
+    
+    This leaves space for 4 flights.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 52d0c638343b2424cae08059f788a02efddee19a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 29 18:39:27 2024 -0800
+
+    altoslib: Add EasyTimer-v2 support
+    
+    Log parsing and idle monitor.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a724a6b223229f8640740874523aee394350e9c6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 29 17:58:02 2024 -0800
+
+    altos/easytimer-v2: Fix up ao_pins.h
+    
+    Fix M25 CS pin (PA10).
+    Fix BMI088 axes
+    Fix MMC5983 axes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a148876e36bc3f84f11fdac57a9f69171e30c058
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 28 21:14:06 2024 -0800
+
+    altos/telelco-v3: Minor UI tweaks
+    
+    "Box"→"Bank" to match docs.
+    Auto-center text instead of pre-computing. Way easier.
+    Scroll found banks during startup.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7977ff5291c6161d37b7c1b1548258df2e58c09e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 28 00:17:27 2024 -0800
+
+    altos/st7565: Set default contrast to 13
+    
+    This seems slightly better than 16. Might need to actually keep this
+    in config space.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ffaee646efded39c3cdb07948823822ce8df8c4e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 23:59:43 2024 -0800
+
+    altos/telelco: Add RSSI display to older devices
+    
+    Show RSSI value on TeleLCO v2.0
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1741039eb3633b3f010ac7fc9e6a055d02aa0a15
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 23:48:51 2024 -0800
+
+    altos/telelco: Add per-box RSSI display screen
+    
+    Shows the last received RSSI value.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 43d1cfd0d1458c2d4c333155902be28bfe17b6ee
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 23:47:45 2024 -0800
+
+    altos/stm32f1: Set beeper pin to 0 while off
+    
+    Make sure we don't let the timer leave it sitting high; the
+    magnetic beepers probably don't do the right thing in that case.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 323a6a44083f865c90c12e93775ac2dbf9352de3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 23:13:17 2024 -0800
+
+    altos/telelco-v3.0: Show backlight/contrast value as percent
+    
+    Provide a bit more feedback about the setting.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit efe964558805a18f070f405377ef0f437ccce237
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 23:12:05 2024 -0800
+
+    altos/st7565: Diff image during update
+    
+    This minimizes the amount of data sent to the device. Mostly this is
+    useful to skip updates that don't change the display at all.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bf7d2aaccd41837dd2e032023107616266e22a6d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 23:11:15 2024 -0800
+
+    altos/telelco: Update backlight/contrast display upon change
+    
+    Don't wait for the 1-second poll interval.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d1fd9055898fb07033b80f3d9d677a97d485fb2f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 23:10:28 2024 -0800
+
+    altos/draw: Add ao_text_width
+    
+    Computes the total advance of the given string, not the width of the
+    resulting ink.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f9103d13f620cb06642aacdff9dcaf05e87d671f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 22:03:00 2024 -0800
+
+    altos/telelco-v3: Add logo to 'info' page
+    
+    Make the info page a bit more fun
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 936ef3907e956b1a42ad54fe5757bd816b20890f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 16:35:33 2024 -0800
+
+    telelco-v3.0: Add info page
+    
+    Show model, version, serial, call and freq
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5cea1324ac4a34a21324c4bb50885ffacb6d29da
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 16:03:35 2024 -0800
+
+    altos/telelco-v3.0: Control LCD backlight with PWM
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5506b6c1f75d639e2d952213a53bc9ee34e5a79c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 16:03:20 2024 -0800
+
+    altos: Add support for backlight control in ao_lco_bits
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7ac9267b156d7bb6e942dd630a9ba142ba0c7a00
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 16:01:20 2024 -0800
+
+    altos/stm32f1: For some reason the DBG registers aren't always available
+    
+    Attempts to discover the device model during flash loading appear to fail
+    for unknown reasons. Switch to basing the memory page size on the total
+    memory size, which should be just as reliable.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ff21e293ee8185b19a30df74ca7a1610625ef465
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 15:31:04 2024 -0800
+
+    altos/telelco-v3.0: Configure SPI GPIO pins to 50MHz
+    
+    Need enough bandwidth to run at 20MHz
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3687628cdd964a5e6729b8c9506efa5e2e9d8a84
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 27 15:30:27 2024 -0800
+
+    altos: Bump ST7565 speed to 20MHz
+    
+    Go as fast as we can manage
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f9e319f5a97df8c0950ec7531d0889cf60e53783
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 26 17:35:43 2024 -0800
+
+    altos/telelco-v3.0: Add contrast setting
+    
+    Provide a UI for setting the LCD contrast.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 23fec388ebdf4230663324e1f0ebfbd79059e841
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 26 17:35:21 2024 -0800
+
+    altos: Build TeleLCO v3.0 bits by default
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 470be148d9a9dcd45609942d17fcf7c1555375f3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 26 16:06:44 2024 -0800
+
+    telelco-v3.0: Minor setup fixes
+    
+    Change beeper to lower tone. Switch LCD SPI to mode 3. Fix the
+    quadrature device ID.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 54a465d062d9a863f14519267f06927d001e2c23
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 26 16:05:45 2024 -0800
+
+    altos: Fix pretend pad range for TeleLCO
+    
+    Off-by-one when setting up the pretend pads.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c8e02fea0b6de30c3929bb1551e7de02b7047f3d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 26 14:03:40 2024 -0800
+
+    altos: Add wiring docs to st-7565 header
+    
+    Write down what worked today.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d2216717adac7f7e917e13085de6ce8118023d5f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 26 11:07:55 2024 -0800
+
+    altos: Add SPI bus parameter to ao_spi_speed
+    
+    The stm32f1 series has two SPI busses and they operate from different
+    base clocks, so the computation of the clock divider to get a
+    particular frequency depends upon which SPI bus is being used.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3fdf2f80ccf3a2b148b63f5fd5de198957719075
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 26 10:59:19 2024 -0800
+
+    stm32f1: make spi speed per-bus
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 044f68d6be05be8b0a6a2432f64b8fb2e94ca956
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 26 10:57:56 2024 -0800
+
+    switch to mode 3 for LCD
+
+commit 9a5cd9e64a66829fb0cd3713c5cbd41085641db3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 24 15:36:34 2024 -0800
+
+    src/stm32f1: Disable extra JTAG pins in ao_clock_init
+    
+    Need to enable AFIO before trying to adjust bits there.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit de37d4a17e8eac517ed00f179c9cd5c09b820dfc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 13 20:57:38 2024 -0800
+
+    altos/telelco-v3.0: Use timer 2 for fast timer
+    
+    We need timer 1 for PWM on the backlight
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e82cf4199761e900ef176a2a6ddd5d7cfc38bc07
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 13 20:57:00 2024 -0800
+
+    altos/stm32f1: Support timers 2,3,4 for fast timer
+    
+    Make the code deal with differences between timer 18 and 234
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d216d17e5317607ced5a2437e5360203647e6eb9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jan 11 16:17:32 2024 -0700
+
+    altos/telelco-v3.0: Battery voltage is on PA2
+    
+    Which is ADC input #2
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e93b73d9adce883e7e4094a06f4de86f5c774567
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jan 11 16:16:58 2024 -0700
+
+    altos/telelco-v3.0: Don't connect PA6 to SPI
+    
+    MISO is not used, and we need the pin for other stuff.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a3fd78224f222adc4cec394a2c8f4153d6e8571c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jan 11 15:12:34 2024 -0700
+
+    altos/stm32f1: Allow PA6 to be left alone in the SPI code
+    
+    This is MISO, which the LCD driver doesn't use. Leave it alone so we
+    can use it for other stuff.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2447293cbaaf7b783744ca2f68bf1769a8798841
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jan 11 15:11:06 2024 -0700
+
+    altos/stm32f1: Poke the ADC harder to get it to sample
+    
+    The ADC requires a bunch of poking to get it started.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 321556be4eda94be40dbefe13f0549ad224c7c33
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 10 23:49:00 2024 -0800
+
+    altos/stm32f1: Turn on ADC during initialization sequence
+    
+    The ADC needs to be left 'on' so that it will actually
+    run the conversion sequence when started.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 65cbf1af7a0c5051c75a7e6a104f3ed17cb629a9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 10 17:27:33 2024 -0700
+
+    altos/telelco-v3.0: Add USB pull up
+    
+    And move the ARM LED to PA9
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fb3cd8d1987b3e4d0a09ece97b3124e954c5f949
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 10 17:26:12 2024 -0700
+
+    altos/stm32f1: Add STM_RCC_CFGR_PLLXTPRE_MASK value
+    
+    Necessary when setting the PLLXTPRE value
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cea7122fc1843b14a179ed9311677f69c10b38eb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 10 17:24:32 2024 -0700
+
+    altos/stm32f1: Set PLLXTPRE value
+    
+    This is the pre-PLL divider value which is needed when we want to
+    use a 16MHz crystal and a 72MHz sysclk
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c8a0811abf59fb55cb17d2159289621d26e887ad
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 10 17:22:57 2024 -0700
+
+    altos/stm32f1: Support beeper on tim3
+    
+    Add a bit more flexibility to the beeper code
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1b8bc4e4aadd367c40d33afefb526f1c60c7d118
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 7 20:13:04 2024 -0800
+
+    altos/telelco*: Make LCO voltage display work on 2.0 and 3.0
+    
+    Fix up the common code to handle LCO voltage display via box 0
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6d3b9ca44ffe43bd6285c37af4ee1d6174be8fcd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 7 19:57:03 2024 -0800
+
+    altos/telelco-v3.0: Fix up search UI
+    
+    Present a progress bar. Show the detected box numbers.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8c5c7dfe66098bc8af47768146739a7cda605968
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 7 19:14:03 2024 -0800
+
+    altos/telelco-v3.0: Add ao_adc_single usage to get battery voltage
+    
+    Poll the ADC to acquire battery voltage data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 37de458faea5f67870b80e3549e37130bf43b11b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 7 19:13:22 2024 -0800
+
+    altos/stm32f1: Add ao_adc_single support
+    
+    This supports polling the ADC for values instead of having
+    them get dumped into a ring at a regular rate.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1760bec9ed19c64f35ea28904cea433d39dead07
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 7 18:58:41 2024 -0800
+
+    altos: Move ao_adc_single.h API header to kernel/
+    
+    THis was in stm, but will be needed by other architectures.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a30f526c42177ed7d4ad239f31b7b5163e16a036
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 7 17:05:34 2024 -0800
+
+    altos/telelco-v3.0: Add some real drawing stuff
+    
+    This required enabling devices with 128kB flash.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit edbe6cca1e6b68bcc90064cd70529a9f9b0f9632
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 7 16:48:43 2024 -0800
+
+    altos/telelco-v3.0: Use eeprom emulation for config storage
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e959061fa17e15dfdd75d35c6c67e68a0e5e98e2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 7 16:47:47 2024 -0800
+
+    altos/stm32f1: Add eeprom emulation using flash
+    
+    Use the last 2kB of flash to emulate eeprom storage for TeleLCO. This should
+    also be useful for EasyMega.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 31756e065828ca5c1779f1349c38d29196f4798d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 7 16:21:40 2024 -0800
+
+    altos: Add initial TeleLCO-v3 bits
+    
+    This might light up some of the hardware...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2733436da522650d4e918b7bc7d65f966f149792
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 7 16:20:42 2024 -0800
+
+    altos: Build in 'draw' directory first
+    
+    This makes various font bits for targets using graphics
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 96cee909d1392f098c22c0122f12e358a7fe8174
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 7 16:20:18 2024 -0800
+
+    altos/stm32f1: Add ao_fast_timer.c
+    
+    Needed for TeleLCO
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 96459ad1231898c743aacf3fbc1afbf92f5579dc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 6 23:24:13 2024 -0800
+
+    altos/telelco-v3: Move USB pullup from PA9 to PA10
+    
+    Production boards will have the pull-up on PA10, and Bdale will hack
+    up the protos to match so we don't have to have separate firmware.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 28c389c08a37010fdbb26e9923d16c3204862e67
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Jan 7 15:55:53 2024 -0700
+
+    fix product name
+
+commit 50dea58c0f2ae87827a1a761ea7868e9798a5fd5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 6 23:16:23 2024 -0800
+
+    altos/telelco-v3: Create initial flash loader setup
+    
+    Copied from easymega-v3 with USB pull-up moved to PA9. Note this
+    assumes the proto boards will have a resistor fitted there, which is
+    not in the artwork.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2a34be23938cf1b3c3662abe2d39492bda9a4be1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 30 00:46:36 2023 -0700
+
+    altos/easymega-v3.0: Switch to STM32F103
+    
+    For some reason, there was easymega v3.0 firmware sitting here, but
+    it was mostly a copy of easymega v2.0 bits. Fix that to support the
+    current design based on the STM32F103 SoC.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4683ef1d97f405fabbc214eb7b83bba15990ff79
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 30 00:45:17 2023 -0700
+
+    drivers: Add real i2c support to mmc5983
+    
+    It only had bit-banging i2c support; go ahead
+    and add "real" i2c support for EasyMega v3
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1cad5ca2525fbe067f897871bc2c4dc45a13e85a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 30 00:05:21 2023 -0700
+
+    altos/stm32f1: Add more IP block drivers
+    
+    adc, beep, exti, i2c
+    
+    Also hooked up data sampling bits in ao_timer.c
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3da00a912b07c77d06785933c92e287ab48b0f79
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 16 10:24:36 2023 -0700
+
+    altos: Fix up stm32f103-nucleo 'lco' demo mode
+    
+    Adjust some layout now that I've got a real screen to play with.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6f7d530b9ca06272354d1b7c05813a2523b24887
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 16 10:23:05 2023 -0700
+
+    altos: Clean up st7565 LCD driver
+    
+    Get rid of AO_ prefix on chip-specific register values.
+    Declare ao_st7565_instructions to take const uint8_t * so
+    it can accept an array in flash.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fab278542c9bb31991a93604a9f646e8cd8285af
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 8 17:14:56 2023 -0800
+
+    altos/stm32f103-nucleo: Draw more of the LCO demo screen
+    
+    Add the box/pad screen.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bfafd5b04f0cca36724f3de0b0f18ffac93be591
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 8 15:54:39 2023 -0800
+
+    altos/stm32f103-nucleo: Drive a screen too
+    
+    Use the ST7565 driver to control an LCD screen.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a080a564b9b54e6b3495d30703c45ba2850b1703
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 8 15:53:58 2023 -0800
+
+    altos/stm32f1: Add DMA and SPI drivers
+    
+    These came from the code for the stm32l15 chips
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b0ea39f7eb18aa73160b0b848a9000892f43c0e7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 8 15:53:03 2023 -0800
+
+    altos/driver: Document setting for AO_ST7565_BIAS
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b2d2a9de490a140e8f2c5c2fba739662b340fb3b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 8 15:52:11 2023 -0800
+
+    altos/draw: Have each font declare ao_glyph_temp as .comm
+    
+    Create temporary glyph drawing space by having every glyph declare how
+    much space it needs and letting the linker allocate the largest
+    possible space.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6394680d1e9ff8596068a73eb43e4adc45722455
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 8 15:50:57 2023 -0800
+
+    altos/draw: Skip line adjustment for vertical/horizontal
+    
+    When e3 is zero, don't adjust the minor position based on
+    the major as it doesn't move.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8e82ecb3eddd992c1d351ad7489ae5cbdb761cea
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 8 15:50:19 2023 -0800
+
+    altos/draw: Build demos with -O3 to catch more bugs
+    
+    The compiler fails to do a lot of checks in -O0
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5af8252cb6cf51611963f4df97eee04741438ea5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 8 15:49:52 2023 -0800
+
+    src/draw: Provide some useful constants
+    
+    The two colors and a stride macro
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d65652ac9027475d32510feac9eb8b57a2080d48
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 8 15:14:53 2023 -0800
+
+    altos: Add ST7565 LCD driver
+    
+    This is the chip used by various NewHaven LCD displays
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 09a3c086dae1cce43c843626a1c624b4e2ad8d04
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 25 22:41:48 2023 -0800
+
+    altos/stm32f103-nucleo: Add boot loader
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cea0f40fff14b9d2085e0026a68b742a4b114cb9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 25 19:24:11 2023 -0800
+
+    stm32f1: Get boot loader working
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 58f013162125b2143a5a9f1c0544cb20817e9524
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Feb 19 16:29:56 2023 -0800
+
+    altos/stm32f1: Start work on self-flash code
+    
+    Looks like this uses the same IP block as the stm32f0 series; copied
+    the code, haven't gotten it working yet.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b23ac7ea00ec26779354869765ced9caa2d05738
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Feb 19 15:45:29 2023 -0800
+
+    altos/stm32f103-nucleo: Use more bits
+    
+    Get the OS running, use the serial for console
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 87b64a15f55ecb20a0f8bb2556e77b45a4d82b82
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Feb 19 15:44:32 2023 -0800
+
+    altos/stm32f1: More stm32f103 work
+    
+    Serial driver works
+    Interrupts work
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d2cb18542f4f6071232bd046fd1b257228c17a25
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Feb 17 23:02:16 2023 -0800
+
+    altos: Start work on stm32f1 support
+    
+    Got clocks working. CPU now running at 72MHz.
+    Got systick timer working. Ticks at 100Hz.
+    Got GPIO working. LED blinks.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c361192dbeabeb434fd6744e575756c012545d1b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 25 22:40:29 2023 -0800
+
+    ao-tools: Add flash utility for stm32f1x chips
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e8b0ac5a17d2fb5a0d99ea86c3a2e1c780d3adf3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 30 12:33:22 2024 -0800
+
+    altos/lpc: Adjust ADC clock from 450kHz to 4.5MHz
+    
+    A missing zero. Also, let products change this value if desired to
+    improve high-impedence performance.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0d4e2f95e628b693e418ac9825e81792acf9d809
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 31 17:47:08 2024 -0800
+
+    Fix configure tests for multi-arch libaltos
+    
+    libaltos builds now require a long list of compilers. Check for all of them
+    before enabling it when building in auto mode.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cd20605c9b3c8bc9cbbea4eb1252c9282a317c94
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Jan 22 16:15:44 2024 -0700
+
+    kernel: conditionalize config "report in feet" on presence of baro sensor
+
+commit d4ac6abeb89282d9d96979b2a735aa4dc1e4c33d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 6 22:09:41 2024 -0800
+
+    altos/easytimer-v2: Initialize logging too
+    
+    Now that the logging bits are all written, let's turn it on.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 14f1e175af85c0ef4539316d5ce049798a878fcb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 6 22:08:54 2024 -0800
+
+    altos: Updating pyro format tried to copy entries with no value
+    
+    Need to check for NO_VALUE before attempting to copy data, otherwise
+    things will go badly.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3f31012645918097dc426cd6ba8763b30e78bab1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 6 22:07:29 2024 -0800
+
+    altos/samd21: Enable stdio for USB by default
+    
+    samd21 usb driver didn't bother to enable stdio for USB for some
+    reason.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e026f1d16eb368d6a27b4c4ffbc4cece3950a07b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 6 17:06:58 2024 -0800
+
+    altos/easytimer-v2: Update comments in flash loader ao_pins.h
+    
+    No code changes, but the comments were wrong.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d69b6738040bf751d024b4daceace7aedee6f7c7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 6 17:03:30 2024 -0800
+
+    altos/easytimer-v2: Add logging bits
+    
+    32-byte records with all data in a single record instead of
+    separate volt/sensor bits.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f2292d66184e7f40336bfc59d2e9388391bd9511
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 16 10:30:24 2023 -0700
+
+    altos: Build easytimer-v2 bits
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5de0bd66edf15aaaf8d404d59e2740b8e8050587
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 16 10:29:10 2023 -0700
+
+    altos: Initial easytimer-v2 bits
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 05839dccf5afa431d93ae6124ab8b76f09bf6b54
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 16 10:25:19 2023 -0700
+
+    altos: Use BMI088 as z-axis accel as needed
+    
+    When there's no high-g accel available, use the 'along' axis
+    to measure acceleration.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 010c65fa82aad9d950df519fa7dc111cfbb02104
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Dec 11 13:44:54 2023 -0800
+
+    fixup seven segment debug code
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 83953c145483f4902e31e22062e9261de4c97aa6
+Merge: 7cb9bbe7 960603c4
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Nov 16 09:28:44 2023 -0700
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 7cb9bbe7a285867d76c9d535709383a425cba7c7
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Nov 16 09:28:21 2023 -0700
+
+    add a paragraph about how to hook up a pressure sensor to an EasyMotor
+
+commit 960603c4a4def375a585b01f8b9356b62893b7f1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 10 11:24:45 2023 -0700
+
+    micropeak: Include firmware in install bits
+    
+    These are useful for people building their own hardware.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a5de950272ddaefa03f964a8d31d21ffc7dccab4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 10 11:19:54 2023 -0700
+
+    doc: Use -optimize with asciidoctorpdf
+    
+    This does some additional compression on the output to make it
+    smaller.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 33b6f363bd1377c2cc55213a15b70c65f729dfb7
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 30 11:52:44 2023 -0600
+
+    update Releasing with learnings from 1.9.17 release
+
+commit 8f3653c4586358bf02f3fc7fa8f71728c2fee01a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 20 12:02:36 2023 -0600
+
+    doc: fix a typo in the staging example
+
+commit 7b2588ee723827b9be2a2d5f287afb1b5d351b23
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 30 10:48:52 2023 -0600
+
+    update ChangeLog for 1.9.17 release
+
 commit 63a8707ee4120148f6ec5cb073b349beefe377fa
 Merge: b071450f a4995df3
 Author: Bdale Garbee <bdale@gag.com>
index 08765810d76c14d6fd0564f390140b42e07bd912..8bb1830a17b50c1ea9b661a7964efa549f714d32 100644 (file)
@@ -19,12 +19,9 @@ fat-install: fat
        cd micropeak && $(MAKE) fat-install
 endif
 
-fat:
-       cd src && $(MAKE) all
-       cd doc && $(MAKE) all
+fat: all-recursive
        cd libaltos && $(MAKE) fat
        cd altoslib && $(MAKE) all
-       cd altosuilib && $(MAKE) all
        cd icon && $(MAKE) fat
        cd altosui && $(MAKE) fat
        cd micropeak && $(MAKE) fat
@@ -57,6 +54,7 @@ fat_altos = \
        src/easymini-v3.0/easymini-v3.0-$(VERSION).ihx \
        src/easymotor-v3/easymotor-v3-$(VERSION).ihx \
        src/easytimer-v1/easytimer-v1-$(VERSION).ihx \
+       src/easytimer-v2/easytimer-v2-$(VERSION).ihx \
        src/telebt-v3.0/telebt-v3.0-$(VERSION).ihx \
        src/telebt-v4.0/telebt-v4.0-$(VERSION).ihx \
        src/teledongle-v3.0/teledongle-v3.0-$(VERSION).ihx \
index 042f2d68448af3d948ffca7e9082d16ade9cb4b2..4e0e2829c0fbe3d3e2e7d7f0e7e7c6a39d92b7be 100644 (file)
--- a/Releasing
+++ b/Releasing
@@ -38,7 +38,7 @@ These are Bdale's notes on how to do a release.
                sudo cowbuilder --update
 
        - make sure fat build environment is up to date
-               sudo apt update && sudo apt upgrade
+               sudo apt update && sudo apt upgrade && sudo apt autoremove
 
        - ensure i386 build support is available, and we have tools to build
          installers for Windows and Mac OS X
@@ -46,9 +46,12 @@ These are Bdale's notes on how to do a release.
                sudo apt update
                sudo apt install genisoimage nsis \
                        gcc-i686-linux-gnu gcc-aarch64-linux-gnu \
-                       gcc-arm-linux-gnueabi gcc-arm-linux-gnueabihf
+                       gcc-arm-linux-gnueabi gcc-arm-linux-gnueabihf \
+                       gcc-mingw-w64-i686-posix gcc-mingw-w64-x86-64-win32
 
-       - make sure jsign is installed so we can sign Windows installers
+       - make sure jsign is installed so we can sign Windows installers:
+       
+       https://github.com/ebourg/jsign/releases/download/5.0/jsign_5.0_all.deb
 
        - make sure ~/web/altusmetrum has no pending pullable commits
 
@@ -115,6 +118,7 @@ These are Bdale's notes on how to do a release.
           src/easymini-v[1-3].0/{*.elf,*.ihx,*.map} \
           src/easymotor-v3/{*.elf,*.ihx,*.map} \
           src/easytimer-v1/{*.elf,*.ihx,*.map} \
+          src/easytimer-v2/{*.elf,*.ihx,*.map} \
           src/telebt-v[3-4].0/{*.elf,*.ihx,*.map} \
           src/teledongle-v3.0/{*.elf,*.ihx,*.map} \
           src/telegps-v[1-3].0/{*.elf,*.ihx,*.map} \
@@ -129,6 +133,7 @@ These are Bdale's notes on how to do a release.
           src/easymini-v[1-3].0/flash-loader/*.elf \
           src/easymotor-v3/flash-loader/*.elf \
           src/easytimer-v1/flash-loader/*.elf \
+          src/easytimer-v2/flash-loader/*.elf \
           src/telebt-v[3-4].0/flash-loader/{*.elf,*.bin,*.map} \
           src/teledongle-v3.0/flash-loader/*.elf \
           src/telegps-v[1-3].0/flash-loader/{*.elf,*.bin,*.map} \
index ce34db7fd67ccc06b01dcb9a25c61dadba3421dc..6b980be6b641dd784e06c020d07a743eea036b0c 100644 (file)
@@ -88,6 +88,9 @@ public class AltosConfigData {
 
        public int              report_feet;
 
+       /* HAS_GPS_MOSAIC */
+       public int              gps_receiver;
+
        /* Storage info replies */
        public int      storage_size;
        public int      storage_erase_unit;
@@ -220,9 +223,13 @@ public class AltosConfigData {
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
                case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
+               case AltosLib.AO_LOG_FORMAT_EASYMEGA_3:
                case AltosLib.AO_LOG_FORMAT_EASYMOTOR:
                        /* ADXL375 */
                        return -value;
+               case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
+                       /* BMI088 */
+                       return -value;
                default:
                        if (product.startsWith("EasyTimer-"))
                                return -value;
@@ -327,6 +334,8 @@ public class AltosConfigData {
 
                report_feet = AltosLib.MISSING;
 
+               gps_receiver = AltosLib.MISSING;
+
                tracker_motion = AltosLib.MISSING;
                tracker_interval = AltosLib.MISSING;
 
@@ -522,6 +531,8 @@ public class AltosConfigData {
 
                try { report_feet = get_int(line, "Report in feet:"); } catch (Exception e) {}
 
+               try { gps_receiver = get_int(line, "GPS receiver:"); } catch (Exception e) {}
+
                /* HAS_TRACKER */
                try {
                        int[] values = get_values(line, "Tracker setting:");
@@ -773,6 +784,9 @@ public class AltosConfigData {
                if (report_feet != AltosLib.MISSING)
                        report_feet = source.report_feet();
 
+               if (gps_receiver != AltosLib.MISSING)
+                       gps_receiver = source.gps_receiver();
+
                /* HAS_TRACKER */
                if (tracker_motion != AltosLib.MISSING)
                        tracker_motion = source.tracker_motion();
@@ -830,6 +844,7 @@ public class AltosConfigData {
                dest.set_beep(beep);
                dest.set_radio_10mw(radio_10mw);
                dest.set_report_feet(report_feet);
+               dest.set_gps_receiver(gps_receiver);
                dest.set_tracker_motion(tracker_motion);
                dest.set_tracker_interval(tracker_interval);
        }
@@ -953,10 +968,13 @@ public class AltosConfigData {
                if (radio_10mw != AltosLib.MISSING)
                        link.printf("c p %d\n", radio_10mw);
 
-               /* HAS_RADIO_10MW */
                if (report_feet != AltosLib.MISSING)
                        link.printf("c u %d\n", report_feet);
 
+               /* HAS_GPS_MOSAIC */
+               if (gps_receiver != AltosLib.MISSING)
+                       link.printf("c g %d\n", gps_receiver);
+
                /* HAS_TRACKER */
                if (tracker_motion != AltosLib.MISSING && tracker_interval != AltosLib.MISSING)
                        link.printf("c t %d %d\n", tracker_motion, tracker_interval);
index 8fb302779d188892d541c211ddaca607754fa685..1de180d30ae3485303e6f705ee4aeb68ea6a10bb 100644 (file)
@@ -124,7 +124,13 @@ public interface AltosConfigValues {
 
        public abstract void set_radio_10mw(int radio_10mw);
 
+       public abstract boolean has_radio();
+
        public abstract int report_feet() throws AltosConfigDataException;
 
-       public abstract void set_report_feet(int radio_10mw);
+       public abstract void set_report_feet(int report_feet);
+
+       public abstract int gps_receiver() throws AltosConfigDataException;
+
+       public abstract void set_gps_receiver(int gps_receiver);
 }
index ded4b36527cbfc92e6ba7beefbadcc9a31a3cbb1..84f7e23d91a7f03ea44d3c3139fd7a61a78d7aa3 100644 (file)
@@ -559,7 +559,7 @@ public class AltosConvert {
 
        public static int beep_freq_to_value(double freq) {
                if (freq == 0)
-                       return 94;
+                       return 0;
                return (int) Math.floor (1.0/2.0 * (24.0e6/32.0) / freq + 0.5);
        }
 
index 2c2089d7ebb90d42a5e583fb2f2ca08d4385fc2a..8f3fa4590e43bcecd060b1d287bf09490a7a23db 100644 (file)
@@ -88,6 +88,7 @@ public class AltosEepromRecordSet implements AltosRecordSet {
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
                case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
+               case AltosLib.AO_LOG_FORMAT_EASYMEGA_3:
                        record = new AltosEepromRecordMega(eeprom);
                        break;
                case AltosLib.AO_LOG_FORMAT_TELEMETRUM:
@@ -111,6 +112,9 @@ public class AltosEepromRecordSet implements AltosRecordSet {
                case AltosLib.AO_LOG_FORMAT_EASYMOTOR:
                        record = new AltosEepromRecordMotor(eeprom);
                        break;
+               case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
+                       record = new AltosEepromRecordTimer(eeprom);
+                       break;
                }
 
                ordered = new TreeSet<AltosEepromRecord>();
diff --git a/altoslib/AltosEepromRecordTimer.java b/altoslib/AltosEepromRecordTimer.java
new file mode 100644 (file)
index 0000000..91dcb6a
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright © 2024 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+package org.altusmetrum.altoslib_14;
+
+public class AltosEepromRecordTimer extends AltosEepromRecord {
+       public static final int record_length = 32;
+
+       private int log_format;
+
+       private int imu_type() {
+               switch (log_format) {
+               case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
+                       return AltosIMU.imu_type_easytimer_v2;
+               default:
+                       return AltosLib.MISSING;
+               }
+       }
+
+       private int imu_model() {
+               switch (log_format) {
+               case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
+                       return AltosLib.model_bmi088;
+               }
+               return AltosLib.MISSING;
+       }
+
+       private boolean sensor_normalized() {
+               switch (log_format) {
+               case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
+                       return true;
+               }
+               return false;
+       }
+
+       private int mag_model() {
+               switch (log_format) {
+               case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
+                       return AltosLib.model_mmc5983;
+               }
+               return AltosLib.MISSING;
+       }
+
+       /* AO_LOG_FLIGHT elements */
+       private int flight() { return data16(0); }
+       private int ground_accel() { return data16(2); }
+       private int ground_pres() { return AltosLib.MISSING; }
+       private int ground_accel_along() { return data16(4); }
+       private int ground_accel_across() { return data16(6); }
+       private int ground_accel_through() { return data16(8); }
+       private int ground_roll() { return data32(12); }
+       private int ground_pitch() { return data32(16); }
+       private int ground_yaw() { return data32(20); }
+
+       /* AO_LOG_STATE elements */
+       private int state() { return data16(0); }
+       private int reason() { return data16(2); }
+
+       /* AO_LOG_SENSOR elements */
+
+       private int accel_along() { return data16(0); }
+       private int accel_across() { return data16(2); }
+       private int accel_through() { return data16(4); }
+       private int gyro_roll() { return data16(6); }
+       private int gyro_pitch() { return data16(8); }
+       private int gyro_yaw() { return data16(10); }
+       private int mag_along() { return data16(12); }
+       private int mag_across() { return data16(14); }
+       private int mag_through() { return data16(16); }
+
+       private int accel() { return -accel_along(); }
+
+       private int v_batt() { return data16(18); }
+       private int v_pbatt() { return data16(20); }
+       private int nsense() { return 2; }
+       private int sense(int i) { return data16(22 + i * 2); }
+       private int pyro() { return data16(26); }
+
+       public void provide_data(AltosDataListener listener, AltosCalData cal_data) {
+               super.provide_data(listener, cal_data);
+
+               cal_data.set_imu_type(imu_type());
+               cal_data.set_imu_model(imu_model());
+               cal_data.set_mag_model(mag_model());
+
+               switch (cmd()) {
+               case AltosLib.AO_LOG_FLIGHT:
+                       cal_data.set_flight(flight());
+                       cal_data.set_ground_accel(ground_accel());
+                       listener.set_accel_ground(cal_data.accel_along(ground_accel_along()),
+                                                 cal_data.accel_across(ground_accel_across()),
+                                                 cal_data.accel_through(ground_accel_through()));
+                       cal_data.set_gyro_zero(ground_roll() / 512.0,
+                                              ground_pitch() / 512.0,
+                                              ground_yaw() / 512.0);
+                       break;
+               case AltosLib.AO_LOG_STATE:
+                       listener.set_state(state());
+                       break;
+               case AltosLib.AO_LOG_SENSOR:
+                       AltosConfigData config_data = eeprom.config_data();
+
+                       int     accel_along = accel_along();
+                       int     accel_across = accel_across();
+                       int     accel_through = accel_through();
+                       int     gyro_roll = gyro_roll();
+                       int     gyro_pitch = gyro_pitch();
+                       int     gyro_yaw = gyro_yaw();
+
+                       int     mag_along = mag_along();
+                       int     mag_across = mag_across();
+                       int     mag_through = mag_through();
+
+                       listener.set_accel(cal_data.accel_along(accel_along),
+                                          cal_data.accel_across(accel_across),
+                                          cal_data.accel_through(accel_through));
+                       listener.set_gyro(cal_data.gyro_roll(gyro_roll),
+                                         cal_data.gyro_pitch(gyro_pitch),
+                                         cal_data.gyro_yaw(gyro_yaw));
+
+                       listener.set_mag(cal_data.mag_along(mag_along),
+                                        cal_data.mag_across(mag_across),
+                                        cal_data.mag_through(mag_through));
+
+                       listener.set_acceleration(cal_data.acceleration(accel()));
+
+                       listener.set_battery_voltage(AltosConvert.mega_battery_voltage(v_batt()));
+                       listener.set_pyro_voltage(AltosConvert.mega_pyro_voltage(v_pbatt()));
+
+                       int nsense = nsense();
+
+                       listener.set_apogee_voltage(AltosConvert.mega_pyro_voltage(sense(nsense-2)));
+                       listener.set_main_voltage(AltosConvert.mega_pyro_voltage(sense(nsense-1)));
+
+                       double voltages[] = new double[nsense-2];
+                       for (int i = 0; i < nsense-2; i++)
+                               voltages[i] = AltosConvert.mega_pyro_voltage(sense(i));
+
+                       listener.set_igniter_voltage(voltages);
+                       listener.set_pyro_fired(pyro());
+                       break;
+               }
+       }
+
+       public AltosEepromRecord next() {
+               int     s = next_start();
+               if (s < 0)
+                       return null;
+               return new AltosEepromRecordTimer(eeprom, s);
+       }
+
+       public AltosEepromRecordTimer(AltosEeprom eeprom, int start) {
+               super(eeprom, start, record_length);
+               log_format = eeprom.config_data().log_format;
+       }
+
+       public AltosEepromRecordTimer(AltosEeprom eeprom) {
+               this(eeprom, 0);
+       }
+}
index b4ba5a563e08c3b3f768dd9cad3b837eae4249db..01ecc1616630fbaca656ee5918a1404c4770cc2d 100644 (file)
@@ -73,11 +73,13 @@ public class AltosIMU implements Cloneable {
                case imu_type_easymega_v1:
                case imu_type_easymega_v2:
                        return counts_per_g_mpu;
-               case  imu_type_telemega_v4:
+               case imu_type_telemega_v4:
                case imu_type_easytimer_v1:
                        return counts_per_g_bmx;
                case imu_type_easymotor_v2:
                        return counts_per_g_adxl;
+               case imu_type_easytimer_v2:
+                       return counts_per_g_bmi088;
                }
 
                return AltosLib.MISSING;
@@ -251,6 +253,8 @@ public class AltosIMU implements Cloneable {
 
        public static final int imu_type_easymotor_v2 = 6;      /* ADXL375 (accel only) */
 
+       public static final int imu_type_easytimer_v2 = 7;      /* BMI088 */
+
        private int accel_across(int imu_type) {
 
                if (accel_across != AltosLib.MISSING)
@@ -274,8 +278,10 @@ public class AltosIMU implements Cloneable {
        }
 
        private int accel_along(int imu_type) {
-               if (accel_along != AltosLib.MISSING)
+               if (accel_along != AltosLib.MISSING) {
+                       System.out.printf("accel along %d\n", accel_along);
                        return accel_along;
+               }
 
                switch (imu_type) {
                case imu_type_telemega_v1_v2:
@@ -391,6 +397,7 @@ public class AltosIMU implements Cloneable {
        private static boolean is_primary_accel(int imu_type) {
                switch (imu_type) {
                case imu_type_easytimer_v1:
+               case imu_type_easytimer_v2:
                        return true;
                default:
                        return false;
@@ -402,6 +409,7 @@ public class AltosIMU implements Cloneable {
                        AltosIMU        imu = new AltosIMU(link);
                        AltosCalData    cal_data = listener.cal_data();
 
+                       System.out.printf("imu_model %d mag_model %d\n", imu.imu_model, imu.mag_model);
                        if (imu_type != AltosLib.MISSING)
                                cal_data.set_imu_type(imu_type);
                        if (imu != null) {
index 90f4f53e0374407ed1d124e61d055b843d418154..b64ba0a4c9ee0828d876e80709229b51f5e877e8 100644 (file)
@@ -40,6 +40,8 @@ class AltosIdler {
        static final int        idle_adxl375 = 10;
        static final int        idle_adxl375_easymotor_v2 = 11;
        static final int        idle_imu = 12;
+       static final int        idle_imu_et_v2 = 13;
+       static final int        idle_imu_em_v3 = 14;
 
        static final int        idle_sensor_tm = 100;
        static final int        idle_sensor_metrum = 101;
@@ -54,6 +56,8 @@ class AltosIdler {
        static final int        idle_sensor_easytimer1 = 110;
        static final int        idle_sensor_easymotor2 = 111;
        static final int        idle_sensor_emini3 = 112;
+       static final int        idle_sensor_etimer2 = 113;
+       static final int        idle_sensor_emega3 = 114;
 
        public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException, TimeoutException, AltosUnknownProduct {
                for (int idler : idlers) {
@@ -79,6 +83,9 @@ class AltosIdler {
                        case idle_imu_et_v1:
                                AltosIMU.provide_data(listener, link, AltosIMU.imu_type_easytimer_v1);
                                break;
+                       case idle_imu_et_v2:
+                               AltosIMU.provide_data(listener, link, AltosIMU.imu_type_easytimer_v2);
+                               break;
                        case idle_imu:
                                AltosIMU.provide_data(listener, link, AltosLib.MISSING);
                                break;
@@ -252,6 +259,12 @@ public class AltosIdleFetch implements AltosDataProvider {
                               AltosIdler.idle_ms5607,
                               AltosIdler.idle_imu_em_v2,
                               AltosIdler.idle_sensor_mega),
+               new AltosIdler("EasyMega-v3",
+                              AltosIdler.idle_adxl375,
+                              AltosIdler.idle_ms5607,
+                              AltosIdler.idle_imu,
+                              AltosIdler.idle_mag,
+                              AltosIdler.idle_sensor_mega),
                new AltosIdler("TeleGPS-v1",
                               AltosIdler.idle_gps,
                               AltosIdler.idle_sensor_tgps1),
@@ -267,6 +280,9 @@ public class AltosIdleFetch implements AltosDataProvider {
                new AltosIdler("EasyMotor-v2",
                               AltosIdler.idle_adxl375_easymotor_v2,
                               AltosIdler.idle_sensor_easymotor2),
+               new AltosIdler("EasyTimer-v2",
+                              AltosIdler.idle_imu_et_v2,
+                              AltosIdler.idle_sensor_easymotor2),
        };
 
        AltosLink               link;
index 5293badce58eee47e6fb109741e7caced1e2be04..fbbfd63c3e7cc7f207ace6907b83ea0e73e4d16a 100644 (file)
@@ -140,6 +140,13 @@ public class AltosLib {
        public final static int product_basestation = 0x10000 + 1;
        public final static int product_altimeter = 0x10000 + 2;
 
+       public final static int gps_builtin = 0;
+       public final static int gps_mosaic = 1;
+
+       public final static String[] gps_receiver_names = {
+               "Builtin", "Mosaic-X5"
+       };
+
        private static class Product {
                final String    name;
                final int       product;
@@ -387,6 +394,8 @@ public class AltosLib {
        public static final int AO_LOG_FORMAT_EASYMOTOR = 20;
        public static final int AO_LOG_FORMAT_TELEMEGA_5 = 21;
        public static final int AO_LOG_FORMAT_TELEMEGA_6 = 22;
+       public static final int AO_LOG_FORMAT_EASYTIMER_2 = 23;
+       public static final int AO_LOG_FORMAT_EASYMEGA_3 = 24;
        public static final int AO_LOG_FORMAT_NONE = 127;
 
        public static final int model_mpu6000 = 0;
@@ -614,6 +623,7 @@ public class AltosLib {
                case product_telegps: return "TeleGPS";
                case product_easymini: return "EasyMini";
                case product_telemini: return "TeleMini";
+               case product_easymega: return "EasyMega";
                case product_easymotor: return "EasyMotor";
                default: return "unknown";
                }
@@ -654,6 +664,7 @@ public class AltosLib {
                case AO_LOG_FORMAT_TELEMEGA_3:
                        return product_telemega;
                case AO_LOG_FORMAT_EASYMEGA_2:
+               case AO_LOG_FORMAT_EASYMEGA_3:
                        return product_easymega;
                case AO_LOG_FORMAT_TELESTATIC:
                        return product_altusmetrum;
index c7f1ac1dfabba9bea522fc55d9c577cf0e9f20d3..6e11f3fd0a03a4bd4e7bee67fe142a0a0f682eb7 100644 (file)
@@ -45,6 +45,8 @@ public class AltosMag implements Cloneable {
                case AltosIMU.imu_type_telemega_v1_v2:
                case AltosIMU.imu_type_easymega_v1:
                        return counts_per_gauss_hmc5883;
+               case AltosIMU.imu_type_easytimer_v2:
+                       return counts_per_gauss_mmc5983;
                }
 
                return AltosIMU.counts_per_gauss(imu_type, mag_model);
diff --git a/altoslib/AltosSensorEasyTimer2.java b/altoslib/AltosSensorEasyTimer2.java
new file mode 100644 (file)
index 0000000..4d8102c
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2020 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_14;
+
+import java.util.concurrent.TimeoutException;
+
+class AltosSensorEasyTimer2 {
+       int             tick;
+       int[]           sense;
+       int             v_batt;
+       int             v_pbatt;
+       int             temp;
+
+       public AltosSensorEasyTimer2() {
+               sense = new int[2];
+       }
+
+       public AltosSensorEasyTimer2(AltosLink link) throws InterruptedException, TimeoutException {
+               this();
+               String[] items = link.adc();
+               for (int i = 0; i < items.length;) {
+                       if (items[i].equals("tick:")) {
+                               tick = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("A:")) {
+                               sense[0] = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("B:")) {
+                               sense[1] = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       if (items[i].equals("batt:")) {
+                               v_batt = Integer.parseInt(items[i+1]);
+                               i += 2;
+                               continue;
+                       }
+                       i++;
+               }
+       }
+
+       static public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException {
+               try {
+                       AltosSensorEasyTimer2   sensor_easytimer2 = new AltosSensorEasyTimer2(link);
+
+                       listener.set_battery_voltage(AltosConvert.easy_timer_voltage(sensor_easytimer2.v_batt));
+
+                       double[]        igniter_voltage = new double[2];
+                       for (int i = 0; i < 2; i++)
+                               igniter_voltage[i] = AltosConvert.easy_timer_voltage(sensor_easytimer2.sense[i]);
+                       listener.set_igniter_voltage(igniter_voltage);
+
+               } catch (TimeoutException te) {
+               }
+       }
+}
+
index a030e2cb773ff8939f8aba53eea16464dbe8cd69..7cb2ef6fbc82ca0dc2c07dd09411a34a1563a80c 100644 (file)
@@ -51,6 +51,7 @@ altoslib_JAVA = \
        AltosEepromRecordMicroPeak2.java \
        AltosEepromRecordMotor.java \
        AltosEepromRecordSet.java \
+       AltosEepromRecordTimer.java \
        AltosEepromChunk.java \
        AltosEepromDownload.java \
        AltosEepromMonitor.java \
@@ -108,6 +109,7 @@ altoslib_JAVA = \
        AltosSensorMM.java \
        AltosSensorEMini.java \
        AltosSensorEasyTimer1.java \
+       AltosSensorEasyTimer2.java \
        AltosSensorTM.java \
        AltosSensorTMini2.java \
        AltosSensorTMini3.java \
index 88f8b0808a92a59c68aab2cf982d43bac124949d..3e5bede5169718a291533600d878fe50589a86fc 100644 (file)
@@ -44,6 +44,7 @@ public class AltosConfigFCUI
        JLabel                  radio_enable_label;
        JLabel                  radio_10mw_label;
        JLabel                  report_feet_label;
+       JLabel                  gps_receiver_label;
        JLabel                  rate_label;
        JLabel                  aprs_interval_label;
        JLabel                  aprs_ssid_label;
@@ -73,6 +74,7 @@ public class AltosConfigFCUI
        JRadioButton            radio_enable_value;
        JRadioButton            radio_10mw_value;
        JComboBox<String>       report_feet_value;
+       JComboBox<String>       gps_receiver_value;
        AltosUIRateList         rate_value;
        JComboBox<String>       aprs_interval_value;
        JComboBox<Integer>      aprs_ssid_value;
@@ -240,7 +242,7 @@ public class AltosConfigFCUI
                return product != null && product.startsWith("EasyTimer");
        }
 
-       boolean has_radio() {
+       public boolean has_radio() {
                return is_telemega() || is_telemetrum() || is_telemini();
        }
 
@@ -353,7 +355,7 @@ public class AltosConfigFCUI
 
        void set_beep_tool_tip() {
                if (beep_value.isVisible())
-                       beep_value.setToolTipText("What frequency the beeper will sound at");
+                       beep_value.setToolTipText("What frequency the beeper will sound at (0 for off)");
                else
                        beep_value.setToolTipText("Older firmware could not select beeper frequency");
        }
@@ -372,6 +374,13 @@ public class AltosConfigFCUI
                        report_feet_value.setToolTipText("Older firmware always beeps max height in meters");
        }
 
+       void set_gps_receiver_tool_tip() {
+               if (gps_receiver_value.isVisible())
+                       gps_receiver_value.setToolTipText("GPS receiver selection");
+               else
+                       gps_receiver_value.setToolTipText("Only TeleMega with new firmware supports alternate GPS receivers");
+       }
+
        /* Build the UI using a grid bag */
        public AltosConfigFCUI(JFrame in_owner, boolean remote) {
                super (in_owner, title, false);
@@ -656,6 +665,32 @@ public class AltosConfigFCUI
                set_report_feet_tool_tip();
                row++;
 
+               /* GPS Receiver */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               gps_receiver_label = new JLabel("GPS Receiver:");
+               pane.add(gps_receiver_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               gps_receiver_value = new JComboBox<String>(AltosLib.gps_receiver_names);
+               gps_receiver_value.setEditable(false);
+               gps_receiver_value.addItemListener(this);
+               pane.add(gps_receiver_value, c);
+               set_gps_receiver_tool_tip();
+               row++;
+
                /* Telemetry Rate */
                c = new GridBagConstraints();
                c.gridx = 0; c.gridy = row;
@@ -1553,6 +1588,32 @@ public class AltosConfigFCUI
                        return AltosLib.MISSING;
        }
 
+       public void set_gps_receiver(int new_gps_receiver) {
+               System.out.printf("set_gps_receiver %d\n", new_gps_receiver);
+               if (new_gps_receiver != AltosLib.MISSING) {
+                       if (new_gps_receiver >= AltosLib.gps_receiver_names.length)
+                               new_gps_receiver = 0;
+                       if (new_gps_receiver < 0) {
+                               gps_receiver_value.setEnabled(false);
+                               new_gps_receiver = 0;
+                       } else {
+                               gps_receiver_value.setEnabled(true);
+                       }
+                       gps_receiver_value.setSelectedIndex(new_gps_receiver);
+               }
+               gps_receiver_value.setVisible(new_gps_receiver != AltosLib.MISSING);
+               gps_receiver_label.setVisible(new_gps_receiver != AltosLib.MISSING);
+
+               set_gps_receiver_tool_tip();
+       }
+
+       public int gps_receiver() {
+               if (gps_receiver_value.isVisible())
+                       return gps_receiver_value.getSelectedIndex();
+               else
+                       return AltosLib.MISSING;
+       }
+
        String[] tracker_motion_values() {
                if (AltosConvert.imperial_units)
                        return tracker_motion_values_ft;
index 1da6408c18fe0f1e6fb03fe91c4397cc0d1b58c6..bfea181074d28805b472e4d2cc493caec6505e81 100644 (file)
@@ -155,7 +155,8 @@ FIRMWARE_EMOTOR_3=$(top_srcdir)/src/easymotor-v3/easymotor-v3-$(VERSION).ihx
 FIRMWARE_EMOTOR=$(FIRMWARE_EMOTOR_3)
 
 FIRMWARE_ETIMER_1=$(top_srcdir)/src/easytimer-v1/easytimer-v1-$(VERSION).ihx
-FIRMWARE_ETIMER=$(FIRMWARE_ETIMER_1)
+FIRMWARE_ETIMER_2=$(top_srcdir)/src/easytimer-v2/easytimer-v2-$(VERSION).ihx
+FIRMWARE_ETIMER=$(FIRMWARE_ETIMER_1) $(FIRMWARE_ETIMER_2)
 
 FIRMWARE_TGPS_1_0=$(top_srcdir)/src/telegps-v1.0/telegps-v1.0-$(VERSION).ihx
 FIRMWARE_TGPS_2_0=$(top_srcdir)/src/telegps-v2.0/telegps-v2.0-$(VERSION).ihx
index ee8251bbe14f3c8404413460e86efef4179309db..fbed399e9adff74c937e2181a326cde886b20c10 100644 (file)
@@ -143,6 +143,7 @@ Section "Firmware"
        File "../src/easymega-v2.0/easymega-v2.0-${VERSION}.ihx"
        File "../src/easymotor-v3/easymotor-v3-${VERSION}.ihx"
        File "../src/easytimer-v1/easytimer-v1-${VERSION}.ihx"
+       File "../src/easytimer-v2/easytimer-v2-${VERSION}.ihx"
        File "../src/telelco-v2.0/telelco-v2.0-${VERSION}.ihx"
        File "../src/telefireeight-v1.0/telefireeight-v1.0-${VERSION}.ihx"
        File "../src/telefireeight-v2.0/telefireeight-v2.0-${VERSION}.ihx"
index 94d80193c2d1b5dbbcc6cffb0cbd7b4aed677729..ae8d6246b04f58115e43a088c40c41b4db2e3ee1 100644 (file)
@@ -81,14 +81,14 @@ public class AltosUIAccelCal
                                public void run() {
                                        switch (phase) {
                                        case AltosAccelCal.phase_antenna_up:
-                                               message.setText("Orient antenna upwards and click on Antenna Up");
+                                               message.setText(String.format ("Orient antenna upwards and click on %s", up_msg()));
                                                antenna_up.setEnabled(true);
                                                setDefaultButton(antenna_up);
                                                antenna_down.setEnabled(false);
                                                ok.setEnabled(false);
                                                break;
                                        case AltosAccelCal.phase_antenna_down:
-                                               message.setText("Orient antenna downwards and click on Antenna Down");
+                                               message.setText(String.format("Orient antenna downwards and click on %s", down_msg()));
                                                antenna_up.setEnabled(false);
                                                antenna_down.setEnabled(true);
                                                setDefaultButton(antenna_down);
@@ -157,6 +157,10 @@ public class AltosUIAccelCal
                        }
                }
        }
+
+       public String up_msg() { return config_values.has_radio() ? "Antenna Up" : "Beeper Up"; }
+       public String down_msg() { return config_values.has_radio() ? "Antenna Down" : "Beeper Down"; }
+
        public AltosUIAccelCal(Frame owner, AltosLink link, AltosConfigValues config_values) {
                super(owner, "Calibrate Accelerometer", true);
 
@@ -193,7 +197,7 @@ public class AltosUIAccelCal
                c.gridheight = 1;
                c.weightx = 0;
                c.weighty = 0;
-               antenna_up = new JButton("Antenna Up");
+               antenna_up = new JButton(up_msg());
                antenna_up.setActionCommand("up");
                antenna_up.setEnabled(false);
                antenna_up.addActionListener(this);
@@ -208,7 +212,7 @@ public class AltosUIAccelCal
                c.gridheight = 1;
                c.weightx = 0;
                c.weighty = 0;
-               antenna_down = new JButton("Antenna Down");
+               antenna_down = new JButton(down_msg());
                antenna_down.setActionCommand("down");
                antenna_down.setEnabled(false);
                antenna_down.addActionListener(this);
index c9174d6a8d520ef6be1ca50af6d9e95b010b3641..c358ec709e1723c75f72a262a909d75f742b0ffc 100644 (file)
@@ -1,3 +1,3 @@
-bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x ao-reset-lpc ao-flash-samd21
+bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x ao-reset-lpc ao-flash-samd21 ao-flash-stm32f1
 
-man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1 ao-reset-lpc.1 ao-flash-samd21.1
+man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1 ao-reset-lpc.1 ao-flash-samd21.1 ao-flash-stm32f1.1
diff --git a/ao-tools/ao-flash/ao-flash-stm32f1 b/ao-tools/ao-flash/ao-flash-stm32f1
new file mode 100755 (executable)
index 0000000..536086e
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+case "$#" in
+0)
+       echo "usage: $0 <filename> ..."
+       exit 1
+       ;;
+esac
+openocd \
+       -f interface/stlink.cfg \
+       -c 'transport select hla_swd' \
+       -f target/stm32f1x.cfg \
+       -c init \
+       -c 'reset halt' \
+       -c "program $1 verify reset" \
+       -c 'shutdown'
diff --git a/ao-tools/ao-flash/ao-flash-stm32f1.1 b/ao-tools/ao-flash/ao-flash-stm32f1.1
new file mode 100644 (file)
index 0000000..28fb189
--- /dev/null
@@ -0,0 +1,36 @@
+.\"
+.\" Copyright © 2022 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-FLASH-LPC 1 "ao-flash-stm32f1" ""
+.SH NAME
+ao-flash-stm32f1 \- flash a program to an STM32F1x-based AltOS device using openocd
+.SH SYNOPSIS
+.B "ao-flash-stm32f1"
+\fIfile.elf\fP
+.SH DESCRIPTION
+.I ao-flash-stm32f1
+loads the specified .elf file into the target device flash memory.
+.SH USAGE
+.I ao-flash-stm32f1
+is a simple script that passes the correct arguments to openocd to
+load a file into the target device via a connected STlink
+debugging dongle.
+.SH "SEE ALSO"
+openocd(1)
+.SH AUTHOR
+Keith Packard
index 3cfbc64d056e4e71b975eec2e46fa6375ebdbab9..998cda30a02c8de3eaf11e09634b7dcbf421b118 100644 (file)
@@ -18,13 +18,13 @@ dnl
 dnl Process this file with autoconf to create configure.
 
 AC_PREREQ(2.57)
-AC_INIT([altos], 1.9.17)
+AC_INIT([altos], 1.9.18)
 ANDROID_VERSION=37
 AC_CONFIG_SRCDIR([src/kernel/ao.h])
 AM_INIT_AUTOMAKE([foreign dist-bzip2])
 AM_MAINTAINER_MODE
 
-RELEASE_DATE=2023-08-30
+RELEASE_DATE=2024-04-28
 AC_SUBST(RELEASE_DATE)
 
 DOC_DATE=`LC_ALL=C date -d $RELEASE_DATE +'%d %b %Y'`
@@ -475,39 +475,20 @@ AC_ARG_ENABLE([multi-arch],
 
 case x"$MULTI_ARCH" in
 xauto)
-       arch=`uname -m`
-       case x"$arch" in
-       xx86_64|xi*86)
-               save_CFLAGS="$CFLAGS"
-               save_LIBS="$LIBS"
-               LIBS="-ldl"
-               CFLAGS="-m64"
-               AC_MSG_CHECKING([if ]$CC[ ]$CFLAGS[ can link programs])
-               AC_LINK_IFELSE([AC_LANG_PROGRAM([])],
-                               [M64_LINK=yes],
-                               [M64_LINK=no])
-               AC_MSG_RESULT([$M64_LINK])
-               CFLAGS="-m32"
-               AC_MSG_CHECKING([if ]$CC[ ]$CFLAGS[ can link programs])
-               AC_LINK_IFELSE([AC_LANG_PROGRAM([])],
-                               [M32_LINK=yes],
-                               [M32_LINK=no])
-               AC_MSG_RESULT([$M32_LINK])
-               CFLAGS="$save_CFLAGS"
-               LIBS="$save_LIBS"
-               case x"$M64_LINK"x"$M32_LINK" in
-               xyesxyes)
-                       MULTI_ARCH=yes
+       MULTI_ARCH=yes
+       for arch in i686-linux-gnu x86_64-linux-gnu aarch64-linux-gnu arm-linux-gnueabi arm-linux-gnueabihf; do
+               crossgcc="$arch"-gcc
+               AC_CHECK_PROG(ARCH_SUPPORTED,$crossgcc,yes,no)
+               case "$ARCH_SUPPORTED" in
+               yes)
                        ;;
                *)
                        MULTI_ARCH=no
                        ;;
                esac
-               ;;
-       *)
-               MULTI_ARCH=no
-               ;;
-       esac
+       done
+       AC_MSG_CHECKING([MULTI_ARCH])
+       AC_MSG_RESULT([$MULTI_ARCH])
        ;;
 xyes|xno)
        ;;
@@ -595,7 +576,7 @@ echo "    AVR support.................: ${HAVE_AVR_CC}"
 echo "    Android support.............: ${HAVE_ANDROID_SDK}"
 echo "    Android release support.....: ${ANDROID_RELEASE}"
 echo "    STlink support..............: ${HAVE_STLINK}"
-echo "    i386 and amd64 libaltos.....: ${MULTI_ARCH}"
+echo "    multi-arch libaltos.........: ${MULTI_ARCH}"
 echo "    install shared mime info....: ${INSTALL_SHARED_MIME_INFO}"
 echo "    Strip jar timestamps........: ${STRIP_NONDETERMINISM}"
 echo ""
index a791a8a31c71e576e6006685bdae9724069443c7..6f7a1bec75a3e01e40cbb69d2ab780a85b3e8eed 100644 (file)
@@ -1,3 +1,126 @@
+altos (1.9.18-1) unstable; urgency=medium
+
+  [ Bdale Garbee ]
+  * doc: fix a typo in the staging example
+  * update Releasing with learnings from 1.9.17 release
+  * set java-version=8 in rules to enable openjdk 21 support, closes: #1052587
+  * add a paragraph about how to hook up a pressure sensor to an EasyMotor
+  * kernel: conditionalize config "report in feet" on presence of baro sensor
+  * fix product name
+  * update ChangeLog for 1.9.18 release
+
+  [ Keith Packard ]
+  * doc: Use -optimize with asciidoctorpdf
+  * micropeak: Include firmware in install bits
+  * fixup seven segment debug code
+  * altos: Use BMI088 as z-axis accel as needed
+  * altos: Initial easytimer-v2 bits
+  * altos: Build easytimer-v2 bits
+  * altos/easytimer-v2: Add logging bits
+  * altos/easytimer-v2: Update comments in flash loader ao_pins.h
+  * altos/samd21: Enable stdio for USB by default
+  * altos: Updating pyro format tried to copy entries with no value
+  * altos/easytimer-v2: Initialize logging too
+  * Fix configure tests for multi-arch libaltos
+  * altos/lpc: Adjust ADC clock from 450kHz to 4.5MHz
+  * ao-tools: Add flash utility for stm32f1x chips
+  * altos: Start work on stm32f1 support
+  * altos/stm32f1: More stm32f103 work
+  * altos/stm32f103-nucleo: Use more bits
+  * altos/stm32f1: Start work on self-flash code
+  * stm32f1: Get boot loader working
+  * altos/stm32f103-nucleo: Add boot loader
+  * altos: Add ST7565 LCD driver
+  * src/draw: Provide some useful constants
+  * altos/draw: Build demos with -O3 to catch more bugs
+  * altos/draw: Skip line adjustment for vertical/horizontal
+  * altos/draw: Have each font declare ao_glyph_temp as .comm
+  * altos/driver: Document setting for AO_ST7565_BIAS
+  * altos/stm32f1: Add DMA and SPI drivers
+  * altos/stm32f103-nucleo: Drive a screen too
+  * altos/stm32f103-nucleo: Draw more of the LCO demo screen
+  * altos: Clean up st7565 LCD driver
+  * altos: Fix up stm32f103-nucleo 'lco' demo mode
+  * altos/stm32f1: Add more IP block drivers
+  * drivers: Add real i2c support to mmc5983
+  * altos/easymega-v3.0: Switch to STM32F103
+  * altos/telelco-v3: Create initial flash loader setup
+  * altos/telelco-v3: Move USB pullup from PA9 to PA10
+  * altos/stm32f1: Add ao_fast_timer.c
+  * altos: Build in 'draw' directory first
+  * altos: Add initial TeleLCO-v3 bits
+  * altos/stm32f1: Add eeprom emulation using flash
+  * altos/telelco-v3.0: Use eeprom emulation for config storage
+  * altos/telelco-v3.0: Add some real drawing stuff
+  * altos: Move ao_adc_single.h API header to kernel/
+  * altos/stm32f1: Add ao_adc_single support
+  * altos/telelco-v3.0: Add ao_adc_single usage to get battery voltage
+  * altos/telelco-v3.0: Fix up search UI
+  * altos/telelco*: Make LCO voltage display work on 2.0 and 3.0
+  * altos/stm32f1: Support beeper on tim3
+  * altos/stm32f1: Set PLLXTPRE value
+  * altos/stm32f1: Add STM_RCC_CFGR_PLLXTPRE_MASK value
+  * altos/telelco-v3.0: Add USB pull up
+  * altos/stm32f1: Turn on ADC during initialization sequence
+  * altos/stm32f1: Poke the ADC harder to get it to sample
+  * altos/stm32f1: Allow PA6 to be left alone in the SPI code
+  * altos/telelco-v3.0: Don't connect PA6 to SPI
+  * altos/telelco-v3.0: Battery voltage is on PA2
+  * altos/stm32f1: Support timers 2,3,4 for fast timer
+  * altos/telelco-v3.0: Use timer 2 for fast timer
+  * src/stm32f1: Disable extra JTAG pins in ao_clock_init
+  * switch to mode 3 for LCD
+  * stm32f1: make spi speed per-bus
+  * altos: Add SPI bus parameter to ao_spi_speed
+  * altos: Add wiring docs to st-7565 header
+  * altos: Fix pretend pad range for TeleLCO
+  * telelco-v3.0: Minor setup fixes
+  * altos: Build TeleLCO v3.0 bits by default
+  * altos/telelco-v3.0: Add contrast setting
+  * altos: Bump ST7565 speed to 20MHz
+  * altos/telelco-v3.0: Configure SPI GPIO pins to 50MHz
+  * altos/stm32f1: For some reason the DBG registers aren't always available
+  * altos: Add support for backlight control in ao_lco_bits
+  * altos/telelco-v3.0: Control LCD backlight with PWM
+  * telelco-v3.0: Add info page
+  * altos/telelco-v3: Add logo to 'info' page
+  * altos/draw: Add ao_text_width
+  * altos/telelco: Update backlight/contrast display upon change
+  * altos/st7565: Diff image during update
+  * altos/telelco-v3.0: Show backlight/contrast value as percent
+  * altos/stm32f1: Set beeper pin to 0 while off
+  * altos/telelco: Add per-box RSSI display screen
+  * altos/telelco: Add RSSI display to older devices
+  * altos/st7565: Set default contrast to 13
+  * altos/telelco-v3: Minor UI tweaks
+  * altos/easytimer-v2: Fix up ao_pins.h
+  * altoslib: Add EasyTimer-v2 support
+  * altos/easytimer-v2: Set default log size to 192kB
+  * altos/easytimer-v2: Fix product name
+  * altosui: Make accel cal dialog say 'beeper' instead of 'antenna'
+  * stm32f1: Clean up some ADC definitions
+  * src/easymini-2.0: Add combined .dfu file for Seeed testing
+  * altoslib: Add EasyMega v3.0 support
+  * altos/stm32f103: Fix continuous ADC code
+  * altos: Build EasyMega v3.0 by defaul
+  * altos/easymega-v3.0: Update i2c pin usage
+  * altos/stm32f1: Grab both TX/RX DMA mutexes while doing I2C
+  * altos/easytimer-v2: Generate combined .ihx file for seeed testing
+  * altos: ao_fec_prepare using wrong value for input len
+  * altos/test: Adjust CRC error rate after FEC fix
+  * altos/test: Add FEC test for simple 'hello' message
+  * altosui: Set the beeper to 0 to disable
+  * altosui: Support gps receiver setting
+  * Add EasyTimer v2 firmware
+  * doc: Add 1.9.18 release notes
+  * Version 1.9.18
+  * Fix up fat build target
+  * altos/draw: Add 'install' target
+  * doc: Add 1.9.18 release notes
+  * Version 1.9.18
+
+ -- Bdale Garbee <bdale@gag.com>  Sun, 28 Apr 2024 19:45:15 -0600
+
 altos (1.9.17-1) unstable; urgency=medium
 
   [ Bdale Garbee ]
diff --git a/debian/patches/fix-typo b/debian/patches/fix-typo
deleted file mode 100644 (file)
index 1363e2e..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/doc/pyro-examples.inc b/doc/pyro-examples.inc
-index 051f0054..1a27a768 100644
---- a/doc/pyro-examples.inc
-+++ b/doc/pyro-examples.inc
-@@ -30,7 +30,7 @@
-               the previous stage has completed providing acceleration, to
-               minimize drag of the sustainer's coast phase before ignition.
-               Recovery, whether the remainder of the flight is nominal or
--              not, usually works best when the states are separated.  So,
-+              not, usually works best when the stages are separated.  So,
-               the "best" way to configure a pyro channel for a separation
-               charge is to just set "after motor number".  For a 2-stage
-               project, set this to "1".  This will cause the pyro channel
index 038f0fbb9594abe64c1a8a7409725f1e51004156..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1 +0,0 @@
-fix-typo
index 960da9ee69f34e148f3a8dc4a861d51e865407f2..2a991377c4e634a2d0bf3600ba6d62017a98a3c0 100755 (executable)
@@ -10,6 +10,7 @@ prebuild:
        echo "not frobbing changelog for official builds"
 
 configure_flags = \
+       --with-java-version=8 \
        --disable-install-shared-mime-info \
        --enable-faketime \
        --disable-multi-arch
index 9a8a5f6084df69e2b8d30328531041de568c46ae..2ddfec488dd6b64e036df77710961f74fc3584ac 100644 (file)
@@ -17,6 +17,7 @@ FAKETIME=TZ=UTC faketime -f '$(RELEASE_DATE) 00:00:00 i0'
 endif
 
 RELNOTES_INC=\
+       release-notes-1.9.18.inc \
        release-notes-1.9.17.inc \
        release-notes-1.9.16.inc \
        release-notes-1.9.15.inc \
@@ -323,7 +324,7 @@ ATTRIBUTES=--attribute="revdate=$(DOC_DATE)" --attribute="version=$(VERSION)"
        asciidoctor $(ATTRIBUTES) -b html5 $*.adoc
 
 .adoc.pdf:
-       asciidoctor-pdf $(ATTRIBUTES) $*.adoc
+       asciidoctor-pdf $(ATTRIBUTES) -a optimize $*.adoc
 
 all:   $(HTML) $(PDF)
 
index 14e5634bb95946ba141ade05d9f88613ea862677..c838a52431b033e86e7cc06d2127ff8be2b9a34f 100644 (file)
@@ -54,7 +54,7 @@ footer:
     left:
       content: '{page-number}'
     right:
-      content: '© 2023 Bdale Garbee and Keith Packard. Creative Commons ShareAlike 3.0 License'
+      content: '© 2024 Bdale Garbee and Keith Packard. Creative Commons ShareAlike 3.0 License'
   verso:
     left:
       content: $footer_recto_right_content
index cb9ef4e2f5f59aab6d248147e27f9b1bb32ef5c4..5a33d7541c34bc72adf8a327c3bc1c552ff4cd42 100644 (file)
@@ -5,7 +5,7 @@ Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>; Bob Finch; Anth
 :revdate: 1 Jan 1970
 :icons:
 :icontype: svg
-:copyright: Bdale Garbee and Keith Packard 2023
+:copyright: Bdale Garbee and Keith Packard 2024
 :doctype: book
 :numbered:
 :stylesheet: am.css
index fca8e9a06da59ea0b9bf9036f01f6c260dcaa3c1..3871d338ea4c58b9035f6e70e57b830527dd1350 100644 (file)
@@ -1,5 +1,13 @@
 [appendix]
 == Release Notes
+       :leveloffset: 2
+       include::release-notes-1.9.18.adoc[]
+
+       <<<<
+       :leveloffset: 2
+       include::release-notes-1.9.17.adoc[]
+
+       <<<<
        :leveloffset: 2
        include::release-notes-1.9.16.adoc[]
 
index 3cacba86e292d6387a2f8972014fe1b1f3244721..a91f5908ae221ad2826125af15ff67441877a538 100644 (file)
@@ -7,7 +7,7 @@ endif::[]
 [license]
 == License
 
-Copyright © 2023 Bdale Garbee and Keith Packard
+Copyright © 2024 Bdale Garbee and Keith Packard
 
 This document is released under the terms of the link:http://creativecommons.org/licenses/by-sa/3.0/[Creative Commons ShareAlike 3.0 License]
 
index 3914bc709b1ffbc0d136211601454f9b54547390..778a914ff96b64686f2687ad413a9ba25482c2d6 100644 (file)
                connector, are readily available through various vendors
                including Amazon, eBay, and AliExpress.
 
+               These pressure sensors have three wires and sometimes a 
+               shield around those wires.  The colors of the wires can vary, 
+               but typically are red for power, black for ground, and green 
+               for the analog output.  Hook red to '+5', green to 'PRES', and
+               black to one of the two 'GND' screws on the board.  The other
+               'GND' screw is available for connecting the shield if one is
+               present.  This is mostly relevant if you're using the product
+               for static testing on the ground with a longer than usual
+               cable. 
+
                To log in-flight chamber pressure, a typical approach
                might be to drill a 1/8" sampling hole all the way
                through the center of the motor's forward closure, then
@@ -68,4 +78,5 @@
                If you aren't up for machining closures yourself, 
                link:http://lokiresearch.com[Loki Research] sells 
                54mm and 75mm "experimental bulkheads" with threaded
-               sensor ports that can be used with snap ring cases.
+               sensor ports that can be used with snap ring cases, 
+               and other related mechanical bits and pieces.
diff --git a/doc/release-notes-1.9.18.inc b/doc/release-notes-1.9.18.inc
new file mode 100644 (file)
index 0000000..3e1544d
--- /dev/null
@@ -0,0 +1,18 @@
+= Release Notes for Version 1.9.18
+include::release-head.adoc[]
+:doctype: article
+
+       Version 1.9.18
+
+       == AltOS
+       
+       * Add support for EasyTimer V2. The new version of this
+          product has on-board storage to log data during flight.
+
+       == AltosUI & TeleGPS application
+
+       * Add support for EasyTimer V2. This includes support for
+          analyizing flight data from the on-board logs.
+
+       * Allow on-board beepers to be disabled by setting the
+         frequency to 0.
index 786f29e3e3a229774482a6ddf63fc380f37f408d..7b98ceafb5f70545f9a9a8f21d029a37aa9a64c0 100644 (file)
@@ -1,5 +1,9 @@
 [appendix]
 == Release Notes
+       :leveloffset: 2
+       include::release-notes-1.9.18.adoc[]
+
+       <<<<
        :leveloffset: 2
        include::release-notes-1.9.17.adoc[]
 
index c00e1b3a235455b71eca0d9ea690392a72c1576f..3e02e5e3580ce606915526f40230b6d8fe11e375 100644 (file)
        |-
        |-
        |3.7-12V
+
+       |EasyTimer v2.0
+       |-
+       |24g
+       |-
+       |BMI088
+       |1MB
+       |-
+       |3.7-12V
        endif::easytimer[]
        
        ifdef::easymotor[]
index 3ef5f9bf6a9231064f99631e3c4e5b288d86c64b..f6c75e240ed0bf33fa1135f7968d8561e12a9be8 100644 (file)
@@ -1,5 +1,13 @@
 [appendix]
 == Release Notes
+       :leveloffset: 2
+       include::release-notes-1.9.18.adoc[]
+
+       <<<<
+       :leveloffset: 2
+       include::release-notes-1.9.17.adoc[]
+
+       <<<<
        :leveloffset: 2
        include::release-notes-1.9.16.adoc[]
 
index bd938e2513ed5ede19b7dae7a1b8f56ed4745fe7..4a93800d287c10c5ed8cb08f40297f939cf8a148 100644 (file)
@@ -93,6 +93,13 @@ LINUX_ICONS  =\
 LINUX_MIMETYPE =\
        $(ICONDIR)/org-altusmetrum-mimetypes.xml
 
+FIRMWARE_MP_0_1=$(top_srcdir)/src/micropeak/micropeak-v0.1-$(VERSION).ihx
+FIRMWARE_MP=$(FIRMWARE_MP_0_1)
+FIRMWARE_MS_1_0=$(top_srcdir)/src/microsplash/microsplash-v1.0-$(VERSION).ihx
+FIRMWARE_MS=$(FIRMWARE_MS_1_0)
+
+FIRMWARE=$(FIRMWARE_MP) $(FIRMWARE_MS)
+
 desktopdir = $(datadir)/applications
 desktop_file = altusmetrum-micropeak.desktop
 desktop_SCRIPTS = $(desktop_file)
@@ -141,6 +148,7 @@ MACOSX_INFO_PLIST=Info.plist
 MACOSX_README=ReadMe-Mac.rtf
 MACOSX_INSTALL=../altosui/install-macosx ../altosui/ask-pass
 MACOSX_FILES=$(FAT_FILES) libaltos.dylib $(MACOSX_INFO_PLIST) $(MACOSX_DRIVERS) $(MACOSX_README) $(DOC) $(MACOSX_ICONS) $(MACOSX_INSTALL)
+MACOSX_EXTRA=$(FIRMWARE)
 
 $(MACOSX_DRIVER_0):
        wget -O $@ $(MACOSX_DRIVER_0_URL)
@@ -154,7 +162,7 @@ WINDOWS_DRIVER=CDM_v2.12.00_WHQL_Certified.exe
 $(WINDOWS_DRIVER):
        wget -O "$(WINDOWS_DRIVER)" "$(WINDOWS_DRIVER_URL)"
 
-WINDOWS_FILES=$(FAT_FILES) altos.dll altos64.dll $(DOC) $(WINDOWS_ICONS) $(WINDOWS_DRIVER)
+WINDOWS_FILES=$(FAT_FILES) $(FIRMWARE) altos.dll altos64.dll $(DOC) $(WINDOWS_ICONS) $(WINDOWS_DRIVER)
 
 if FATINSTALL
 
@@ -323,7 +331,7 @@ $(LINUX_SH): $(LINUX_DIST) $(srcdir)/../altosui/linux-install.sh
        sed 's/AltOS/MicroPeak/g' $(srcdir)/../altosui/linux-install.sh | cat - $(LINUX_DIST) > $@
        chmod +x $@
 
-$(MACOSX_DIST): $(MACOSX_FILES)
+$(MACOSX_DIST): $(MACOSX_FILES) $(MACOSX_EXTRA) Makefile
        -rm -f $@
        -rm -rf macosx
        mkdir macosx
@@ -334,7 +342,7 @@ $(MACOSX_DIST): $(MACOSX_FILES)
        cp -a $(DOC) macosx/Doc
        cp -p Info.plist macosx/MicroPeak.app/Contents
        cp -p $(MACOSX_DRIVERS) macosx
-       mkdir -p macosx/MicroPeak.app/Contents/Resources/Java
+       mkdir -p macosx/MicroPeak-$(VERSION) macosx/MicroPeak.app/Contents/Resources/Java
        cp -p $(MACOSX_ICONS) macosx/MicroPeak.app/Contents/Resources
        cp -p $(FATJAR) macosx/MicroPeak.app/Contents/Resources/Java/micropeak.jar
        cp -p libaltos.dylib macosx/MicroPeak.app/Contents/Resources/Java
@@ -342,6 +350,7 @@ $(MACOSX_DIST): $(MACOSX_FILES)
        cp -p $(ALTOSUILIB_CLASS) macosx/MicroPeak.app/Contents/Resources/Java
        cp -p $(JFREECHART_CLASS) macosx/MicroPeak.app/Contents/Resources/Java
        cp -p $(JCOMMON_CLASS) macosx/MicroPeak.app/Contents/Resources/Java
+       cp -p $(MACOSX_EXTRA) macosx/MicroPeak-$(VERSION)
        genisoimage -D -V MicroPeak-$(VERSION) -no-pad -r -apple -o $@ macosx
 
 $(WINDOWS_DIST): $(WINDOWS_FILES) micropeak-windows.nsi
index 34f5908d38a15c1c9eb79bf6a909dc38e74ee44d..6c5133daa67dcab7105777d6dd6643d29442d385 100644 (file)
@@ -117,6 +117,14 @@ Section "FTDI USB Driver"
        ExecWait $2
 SectionEnd
 
+Section "Firmware"
+
+       SetOutPath $INSTDIR
+       File "../src/micropeak/micropeak-v0.1-${VERSION}.ihx"
+       File "../src/microsplash/microsplash-v1.0-${VERSION}.ihx"
+
+SectionEnd
+
 Section "Documentation"
 
        SetOutPath $INSTDIR
index 78638a1e9a90d502f46555e2dc2b258cc00f6619..c89ac0e0b52ea36094529a9c04fe10881eb21370 100644 (file)
@@ -21,6 +21,7 @@ ARMM3DIRS=\
        easymega-v2.0 easymega-v2.0/flash-loader \
        easymotor-v3 easymotor-v3/flash-loader \
        easytimer-v1 easytimer-v1/flash-loader \
+       easytimer-v2 easytimer-v2/flash-loader \
        telemega-v0.1 telemega-v0.1/flash-loader \
        telemega-v1.0 telemega-v1.0/flash-loader \
        telemega-v2.0 telemega-v2.0/flash-loader \
@@ -55,12 +56,14 @@ ARMM0DIRS=\
        telemini-v3.0 telemini-v3.0/flash-loader \
        easymini-v2.0 easymini-v2.0/flash-loader \
        easymini-v3.0 easymini-v3.0/flash-loader \
+       telelco-v3.0 telelco-v3.0/flash-loader \
+       easymega-v3.0 easymega-v3.0/flash-loader \
        micropeak-v2.0
 
 AVRDIRS=\
        micropeak microkite microsplash
 
-SUBDIRS=
+SUBDIRS=draw
 
 ifeq ($(strip $(HAVE_ARM_M3_CC)),yes)
 SUBDIRS+=$(ARMM3DIRS)
index 5f70a6acf04c425f52147b6739fe4b1665d49a75..746178a044e82c133854536925d862636cf4f0f3 100644 (file)
@@ -95,7 +95,9 @@ FONT_SRCS=$(BDFS:.bdf=.c)
 .bdf.c:
        nickle font-convert -o $@ $<
 
-all: ao_font.h ao_logo.h lco-test line-test
+all: ao_font.h ao_logo.h
+
+test: lco-test line-test
 
 $(FONT_SRCS): font-convert
 
@@ -125,7 +127,7 @@ LINE_TEST_OBJS=$(LINE_TEST_SRCS:.c=.o)
 
 TEST_LIBS=-lXrender -lXext -lX11 -lm -Wl,--gc-sections
 
-CFLAGS=-O0 -g $(WARN_FLAGS) -DVALIDATE -I.
+CFLAGS=-O3 -g $(WARN_FLAGS) -DVALIDATE -I.
 
 HEADERS=\
        ao_draw.h \
@@ -149,3 +151,5 @@ $(LINE_TEST_OBJS): $(HEADERS)
 
 clean:
        rm -f $(LCO_TEST_OBJS) ao_font.h ao_logo.h $(FONT_SRCS)
+
+install:
index 29afef9cb043fa3189c6c410ef4bac4e1ed6c325..96e334981bcf91825b564deb77e48ebca5dad558 100644 (file)
@@ -28,6 +28,8 @@ struct ao_bitmap {
        struct ao_box   damage;
 };
 
+#define AO_BITMAP_STRIDE(width)        (((width) + 31) >> 5)
+
 struct ao_coord {
        float   x, y;
 };
@@ -101,6 +103,14 @@ struct ao_glyph_metrics {
        int8_t  advance;
 };
 
+struct ao_text_metrics {
+       int16_t width;
+       int16_t height;
+       int16_t x_off;
+       int16_t y_off;
+       int16_t advance;
+};
+
 struct ao_font {
        const uint8_t   *bytes;
        const uint16_t  *pos;
@@ -158,15 +168,25 @@ ao_poly(struct ao_bitmap          *dst,
        uint32_t                        fill,
        uint8_t                         rop);
 
-void
+int16_t
 ao_text(struct ao_bitmap       *dst,
        const struct ao_font    *font,
        int16_t                 x,
        int16_t                 y,
-       char                    *string,
+       const char              *string,
        uint32_t                fill,
        uint8_t                 rop);
 
+int16_t
+ao_text_width(const struct ao_font     *font,
+             const char                *string);
+
+void
+ao_logo_poly(struct ao_bitmap          *dst,
+            const struct ao_transform  *transform,
+            uint32_t                   fill,
+            uint8_t                    rop);
+
 void
 ao_logo(struct ao_bitmap               *dst,
        const struct ao_transform       *transform,
@@ -209,4 +229,7 @@ extern const struct ao_transform ao_identity;
 #define AO_NAND          0xe   /* NOT src OR NOT dst */
 #define AO_SET           0xf   /* 1 */
 
+#define AO_WHITE       0xffffffff
+#define AO_BLACK       0x00000000
+
 #endif /* _AO_DRAW_H_ */
index 14e1a3c1a591aec6b2cdc5341e08f57d2e01fca2..38314542386f42f503a42699765743bd6785ce47 100644 (file)
@@ -209,26 +209,25 @@ ao_clip_line(struct ao_cc *c, struct ao_cbox *b)
         * isn't symmetrical when the line passes exactly between
         * two pixels, we have to pick which one gets drawn
         */
-       int32_t adj_min;
-
        if (c->e3) {
+               int32_t adj_min;
+
                if (!c->first)
                        adj_min = div_ceil(c->e + adjust_major * c->e1, -c->e3);
                else
                        adj_min = div_floor_plus_one(c->e + adjust_major * c->e1, -c->e3);
-       }
 
-       if (adj_min < adjust_minor) {
-               if (c->e1) {
-                       if (c->first)
-                               adjust_major = div_ceil(c->e - adjust_minor * c->e3, c->e1);
-                       else
-                               adjust_major = div_floor_plus_one(c->e - adjust_minor * c->e3, c->e1);
+               if (adj_min < adjust_minor) {
+                       if (c->e1) {
+                               if (c->first)
+                                       adjust_major = div_ceil(c->e - adjust_minor * c->e3, c->e1);
+                               else
+                                       adjust_major = div_floor_plus_one(c->e - adjust_minor * c->e3, c->e1);
+                       }
+               } else {
+                       adjust_minor = adj_min;
                }
-       } else {
-               adjust_minor = adj_min;
        }
-
        c->e = (int16_t) (c->e + adjust_major * c->e1 + adjust_minor * c->e3);
 
        c->major = (int16_t) (c->major + c->sign_major * adjust_major);
index 747332765839ba05bd3da05b4176ecd6a5614685..53b9a3c289400eabde5dcedd0392aadf8545d50b 100644 (file)
 
 #define ARRAYSIZE(a)   (sizeof(a) / sizeof((a)[0]))
 
+void
+ao_logo_poly(struct ao_bitmap          *dst,
+            const struct ao_transform  *transform,
+            uint32_t                   fill,
+            uint8_t                    rop)
+{
+       if (!transform)
+               transform = &ao_identity;
+       ao_poly(dst, ao_logo_top, ARRAYSIZE(ao_logo_top), transform, fill, rop);
+       ao_poly(dst, ao_logo_bottom, ARRAYSIZE(ao_logo_bottom), transform, fill, rop);
+}
+
 void
 ao_logo(struct ao_bitmap               *dst,
        const struct ao_transform       *transform,
@@ -33,8 +45,7 @@ ao_logo(struct ao_bitmap              *dst,
        int16_t name_x = ao_t_xi(ao_logo_width, 0.0f, transform);
        int16_t name_y1 = ao_t_yi(ao_logo_width, 0.5f, transform);
        int16_t name_y2 = ao_t_yi(ao_logo_width, 0.98f, transform);
-       ao_poly(dst, ao_logo_top, ARRAYSIZE(ao_logo_top), transform, fill, rop);
-       ao_poly(dst, ao_logo_bottom, ARRAYSIZE(ao_logo_bottom), transform, fill, rop);
+       ao_logo_poly(dst, transform, fill, rop);
        ao_text(dst, font, name_x, name_y1, "Altus", fill, rop);
        ao_text(dst, font, name_x, name_y2, "Metrum", fill, rop);
 }
index cc43b362cdde9e43d8d142e90fafe486e05e1cc5..8f5307ab99e474a76292dc2fe45378f49bbf8b5b 100644 (file)
 #include <string.h>
 #include <stdio.h>
 
-void
+extern uint32_t ao_glyph_temp[];
+
+static struct ao_bitmap        src_bitmap = {
+       .base = ao_glyph_temp,
+};
+
+int16_t
 ao_text(struct ao_bitmap       *dst,
        const struct ao_font    *font,
        int16_t                 x,
        int16_t                 y,
-       char                    *string,
+       const char              *string,
        uint32_t                fill,
        uint8_t                 rop)
 {
        int16_t         glyph_stride = ao_stride(font->max_width);
-       uint32_t        src[glyph_stride * font->max_height];
        char            c;
        int             h;
        int16_t         x_off = 0, y_off = 0, advance = 0;
        int16_t         byte_width = 0;
 
-       struct ao_bitmap        src_bitmap = {
-               .base = src,
-       };
-
        rop = (rop & 3) | 0x4;
 
        if ((fill&1) == 0)
@@ -67,7 +68,7 @@ ao_text(struct ao_bitmap      *dst,
                }
 
                for (h = 0; h < src_bitmap.height; h++)
-                       memcpy(&src[h * src_bitmap.stride], &bytes[h * byte_width], (size_t) byte_width);
+                       memcpy(&ao_glyph_temp[h * src_bitmap.stride], &bytes[h * byte_width], (size_t) byte_width);
 
                ao_copy(dst,
                        x + x_off, y - y_off, src_bitmap.width, src_bitmap.height,
@@ -75,4 +76,5 @@ ao_text(struct ao_bitmap      *dst,
                        0, 0, rop);
                x += advance;
        }
+       return x;
 }
diff --git a/src/draw/ao_text_width.c b/src/draw/ao_text_width.c
new file mode 100644 (file)
index 0000000..9f08bd0
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2024 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao_draw.h>
+#include <ao_draw_int.h>
+#include "ao_font.h"
+#include <string.h>
+#include <stdio.h>
+
+int16_t
+ao_text_width(const struct ao_font     *font,
+             const char                *string)
+{
+       char            c;
+       int16_t         x = 0;
+
+       while ((c = *string++)) {
+               if (font->metrics) {
+                       x += font->metrics[(uint8_t) c].advance;
+               } else {
+                       x += font->max_width;
+               }
+       }
+       return x;
+}
index 891331291b6c179235f8748d2d5d9c396deaa57f..8feafe528127fd0f1fc6a529636873a42331830d 100755 (executable)
@@ -195,6 +195,8 @@ void print_font(file out, font_t font, string font_name) {
        fprintf(out, "\t.max_height = %d,\n", max_height);
        fprintf(out, "\t.ascent = %d,\n", font.ascent);
        fprintf(out, "};\n");
+       int max_stride = (max_width + 31) >> 5;
+       fprintf(out, "__asm__(\".balign 4\\n.comm ao_glyph_temp 0x%x\");\n", max_stride * max_height * 4);
 }
 
 string
index 9e5666cc5d6dcb83ea11697e676ba3b544555f01..70bf92a5f1fb72e6a44d0f711de58623a711a222 100644 (file)
@@ -26,7 +26,7 @@
 #define PRINTD(l,...)
 #endif
 
-#define AO_ADXL375_SPI_SPEED   ao_spi_speed(5000000)
+#define AO_ADXL375_SPI_SPEED   ao_spi_speed(AO_ADXL375_SPI_INDEX, 5000000)
 
 struct ao_adxl375_sample       ao_adxl375_current;
 
index d03fceab65845ea6aad79fec4ba83c1889b9b4b5..96e995f056035d2d7fa305e690f30a11fb6bce2b 100644 (file)
@@ -20,7 +20,7 @@
 #include <ao_bmi088.h>
 #include <ao_data.h>
 
-#define AO_BMI088_SPI_SPEED    ao_spi_speed(100000)
+#define AO_BMI088_SPI_SPEED    ao_spi_speed(AO_BMI088_SPI_BUS, 100000)
 
 #define ao_bmi088_spi_get()    ao_spi_get(AO_BMI088_SPI_BUS, AO_BMI088_SPI_SPEED)
 #define ao_bmi088_spi_put()    ao_spi_put(AO_BMI088_SPI_BUS)
index eabe0783253d05c788ff97c2cfc4cf9d12abe9c0..0df30f9428da8c24c53ef982c2b17e8324c67997 100644 (file)
@@ -24,7 +24,7 @@ static uint8_t        ao_bmx160_configured;
 
 static struct ao_bmm150_trim ao_bmm150_trim;
 
-#define AO_BMX160_SPI_SPEED    ao_spi_speed(10000000)
+#define AO_BMX160_SPI_SPEED    ao_spi_speed(AO_BMX160_SPI_BUS, 10000000)
 
 #define ao_bmx160_spi_get()    ao_spi_get(AO_BMX160_SPI_BUS, AO_BMX160_SPI_SPEED)
 #define ao_bmx160_spi_put()    ao_spi_put(AO_BMX160_SPI_BUS)
index 133f98036511735474d34a7079c67f74aee6a0a5..fe3c24c6adc5d59e59ebec32334f5ec53312556c 100644 (file)
@@ -42,7 +42,7 @@ extern const uint32_t ao_radio_cal;
 
 #define FOSC   32000000
 
-#define AO_CC1120_SPI_SPEED    ao_spi_speed(6100000)   /* 6.1MHz max with 32MHz osc */
+#define AO_CC1120_SPI_SPEED    ao_spi_speed(AO_CC1120_SPI_BUS, 6100000)        /* 6.1MHz max with 32MHz osc */
 
 #define ao_radio_try_select(task_id)   ao_spi_try_get_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS,AO_CC1120_SPI_SPEED, task_id)
 #define ao_radio_select()      ao_spi_get_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS,AO_CC1120_SPI_SPEED)
index 65ab576712043c84a49290af143dbf4d9212c66c..606a18049fa260686edda99602dfee906d9ca21d 100644 (file)
@@ -39,7 +39,7 @@ static uint8_t ao_radio_abort;                /* radio operation should abort */
 
 #define FOSC   26000000
 
-#define AO_CC115L_SPI_SPEED    ao_spi_speed(6500000)   /* for back-to-back access */
+#define AO_CC115L_SPI_SPEED    ao_spi_speed(AO_CC115L_SPI_BUS, 6500000)        /* for back-to-back access */
 
 #define ao_radio_select()      ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_CC115L_SPI_SPEED)
 #define ao_radio_deselect()    ao_spi_put_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS)
index b295d054096bcf6c299486d2814e8cde39d5a9b2..ed1d298e1b4a8c100950da114c7ab45ed8abbce0 100644 (file)
@@ -53,7 +53,7 @@ extern const uint32_t ao_radio_cal;
 #define FOSC   40000000
 #endif
 
-#define AO_CC1200_SPI_SPEED    ao_spi_speed(7700000)   /* 7.7MHz max for extended memory reads */
+#define AO_CC1200_SPI_SPEED    ao_spi_speed(AO_CC1200_SPI_BUS, 7700000)        /* 7.7MHz max for extended memory reads */
 
 #define ao_radio_select()      ao_spi_get_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS,AO_CC1200_SPI_SPEED)
 #define ao_radio_deselect()    ao_spi_put_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS)
index 2315b63f43d5a87ad91ea095386da5d838f4e283..bda7805f30219c8cbd57bf22299e2c26bfa82b1f 100644 (file)
@@ -28,7 +28,7 @@
 #error HAS_COMPANION not set in ao_companion.c
 #endif
 
-#define AO_COMPANION_SPI_SPEED ao_spi_speed(200000)
+#define AO_COMPANION_SPI_SPEED ao_spi_speed(AO_COMPANION_SPI_BUS, 200000)
 
 #define COMPANION_SELECT()     do {                    \
                ao_spi_get_bit(AO_COMPANION_CS_PORT,    \
index bc8f7b48f7788949ba6ea1cdcb4314b19dddb668..f6bd630d186fa852be3aa450f0637e53e0654083 100644 (file)
@@ -34,8 +34,6 @@
 #define AO_LCO_DRAG_RACE_START_TIME    AO_SEC_TO_TICKS(5)
 #define AO_LCO_DRAG_RACE_STOP_TIME     AO_SEC_TO_TICKS(2)
 
-#define AO_LCO_BOX_DRAG                0x1000
-
 /* UI values */
 static AO_TICK_TYPE    ao_lco_fire_tick;
 static uint8_t ao_lco_fire_down;
@@ -43,7 +41,7 @@ static uint8_t        ao_lco_fire_down;
 static uint8_t ao_lco_display_mutex;
 
 void
-ao_lco_show_pad(uint8_t pad)
+ao_lco_show_pad(int8_t pad)
 {
        ao_mutex_get(&ao_lco_display_mutex);
        ao_seven_segment_set(AO_LCO_PAD_DIGIT, (uint8_t) (pad | (ao_lco_drag_race << 4)));
@@ -68,7 +66,7 @@ ao_lco_show_pad(uint8_t pad)
                                 (0 << 6))
 
 void
-ao_lco_show_box(uint16_t box)
+ao_lco_show_box(int16_t box)
 {
        ao_mutex_get(&ao_lco_display_mutex);
        if (box == AO_LCO_BOX_DRAG) {
@@ -103,24 +101,13 @@ ao_lco_show(void)
                ao_lco_show_voltage(ao_pad_query.battery);
        } else {
                if (ao_lco_box == AO_LCO_BOX_DRAG)
-                       ao_lco_show_pad(ao_lco_drag_race);
+                       ao_lco_show_pad((int8_t) ao_lco_drag_race);
                else
                        ao_lco_show_pad(ao_lco_pad);
                ao_lco_show_box(ao_lco_box);
        }
 }
 
-uint8_t
-ao_lco_box_present(uint16_t box)
-{
-       if (box == AO_LCO_BOX_DRAG)
-               return 1;
-
-       if (box >= AO_PAD_MAX_BOXES)
-               return 0;
-       return (ao_lco_box_mask[AO_LCO_MASK_ID(box)] >> AO_LCO_MASK_SHIFT(box)) & 1;
-}
-
 static struct ao_task  ao_lco_drag_task;
 static uint8_t         ao_lco_drag_active;
 
@@ -187,29 +174,6 @@ ao_lco_drag_monitor(void)
        }
 }
 
-static void
-ao_lco_step_box(int8_t dir)
-{
-       int32_t new_box = (int32_t) ao_lco_box;
-       do {
-               if (new_box == AO_LCO_BOX_DRAG) {
-                       if (dir < 0)
-                               new_box = ao_lco_max_box;
-                       else
-                               new_box = ao_lco_min_box;
-               } else {
-                       new_box += dir;
-                       if (new_box > ao_lco_max_box)
-                               new_box = AO_LCO_BOX_DRAG;
-                       else if (new_box < ao_lco_min_box)
-                               new_box = AO_LCO_BOX_DRAG;
-               }
-               if (new_box == (int32_t) ao_lco_box)
-                       break;
-       } while (!ao_lco_box_present((uint16_t) new_box));
-       ao_lco_set_box((uint16_t) new_box);
-}
-
 static void
 ao_lco_input(void)
 {
index 03c810be49fe7f0bfe6a9210323ff342735ad93d..176f83837898e48366f28ce19f66e18f2af7880b 100644 (file)
@@ -38,17 +38,88 @@ extern uint8_t      ao_lco_debug;
 extern uint8_t ao_lco_drag_race;       /* true when drag race mode enabled */
 #endif
 
-extern uint8_t ao_lco_pad;             /* Currently selected pad */
-extern uint16_t        ao_lco_box;             /* Currently selected box */
+extern int8_t  ao_lco_pad;             /* Currently selected pad */
+extern int16_t ao_lco_box;             /* Currently selected box */
 
 extern uint8_t ao_lco_armed;           /* armed mode active */
 extern uint8_t ao_lco_firing;          /* fire button pressed */
 
 extern struct ao_pad_query     ao_pad_query;   /* Last received QUERY from pad */
 
+#ifdef AO_LCO_DRAG_RACE_BOX
+#define AO_LCO_BOX_DRAG                0               /* Box number to enable drag race mode (old LCO bits) */
+#define AO_LCO_BOX_FIRST       AO_LCO_BOX_DRAG
+#else
+# define AO_LCO_LCO_VOLTAGE    0               /* Box number to show LCO voltage */
+# ifdef AO_LCO_HAS_INFO
+#  define AO_LCO_INFO          -3
+#  ifndef AO_LCO_BOX_FIRST
+#   define AO_LCO_BOX_FIRST AO_LCO_INFO
+#  endif
+# endif
+# ifdef AO_LCO_HAS_BACKLIGHT
+#   define AO_LCO_BACKLIGHT    -2
+#   ifndef AO_LCO_BOX_FIRST
+#    define AO_LCO_BOX_FIRST AO_LCO_BACKLIGHT
+#   endif
+# endif
+# ifdef AO_LCO_HAS_CONTRAST
+#  define AO_LCO_CONTRAST      -1
+#  ifndef AO_LCO_BOX_FIRST
+#   define AO_LCO_BOX_FIRST    AO_LCO_CONTRAST
+#  endif
+# endif
+# ifndef AO_LCO_BOX_FIRST
+#  define AO_LCO_BOX_FIRST     AO_LCO_LCO_VOLTAGE
+# endif
+#endif
 #define AO_LCO_PAD_VOLTAGE     0               /* Pad number to show box voltage */
-
-extern uint16_t        ao_lco_min_box, ao_lco_max_box;
+#define AO_LCO_PAD_RSSI                -1              /* Pad number to show box RSSI */
+#define AO_LCO_PAD_FIRST       AO_LCO_PAD_RSSI
+
+static inline bool
+ao_lco_box_pseudo(int16_t box)
+{
+       switch (box) {
+#ifdef AO_LCO_LCO_VOLTAGE
+       case AO_LCO_LCO_VOLTAGE:
+               return true;
+#endif
+#ifdef AO_LCO_DRAG_RACE_BOX
+       case AO_LCO_BOX_DRAG:
+               return true;
+#endif
+#ifdef AO_LCO_CONTRAST
+       case AO_LCO_CONTRAST:
+               return true;
+#endif
+#ifdef AO_LCO_BACKLIGHT
+       case AO_LCO_BACKLIGHT:
+               return true;
+#endif
+#ifdef AO_LCO_INFO
+       case AO_LCO_INFO:
+               return true;
+#endif
+       default:
+               return false;
+       }
+}
+
+static inline bool
+ao_lco_pad_pseudo(int8_t pad)
+{
+       switch (pad) {
+       case AO_LCO_PAD_VOLTAGE:
+               return true;
+       case AO_LCO_PAD_RSSI:
+               return true;
+       default:
+               return false;
+       }
+}
+
+extern int16_t ao_lco_min_box, ao_lco_max_box;
 
 #define AO_LCO_MASK_SIZE(n)    (((n) + 7) >> 3)
 #define AO_LCO_MASK_ID(n)      ((n) >> 3)
@@ -56,6 +127,11 @@ extern uint16_t     ao_lco_min_box, ao_lco_max_box;
 
 extern uint8_t ao_lco_box_mask[AO_LCO_MASK_SIZE(AO_PAD_MAX_BOXES)];
 
+#define AO_LCO_VALID_LAST      1
+#define AO_LCO_VALID_EVER      2
+
+extern uint8_t ao_lco_valid[AO_PAD_MAX_BOXES];         /* AO_LCO_VALID bits per box */
+
 /*
  * Shared functions
  */
@@ -67,19 +143,22 @@ void
 ao_lco_update(void);
 
 uint8_t
-ao_lco_pad_present(uint16_t box, uint8_t pad);
+ao_lco_pad_present(int16_t box, int8_t pad);
 
-uint8_t
-ao_lco_pad_first(uint16_t box);
+int8_t
+ao_lco_pad_first(int16_t box);
 
 void
-ao_lco_set_pad(uint8_t new_pad);
+ao_lco_set_pad(int8_t new_pad);
 
 void
 ao_lco_step_pad(int8_t dir);
 
 void
-ao_lco_set_box(uint16_t new_box);
+ao_lco_set_box(int16_t new_box);
+
+void
+ao_lco_step_box(int8_t dir);
 
 void
 ao_lco_set_armed(uint8_t armed);
@@ -99,7 +178,7 @@ ao_lco_search(void);
 void
 ao_lco_monitor(void);
 
-extern uint8_t                 ao_lco_drag_beep_count;
+extern int8_t                  ao_lco_drag_beep_count;
 
 /* enable drag race mode */
 void
@@ -119,7 +198,7 @@ ao_lco_drag_warn_check(AO_TICK_TYPE now, AO_TICK_TYPE delay);
 
 /* Request 'beeps' additional drag race beeps */
 void
-ao_lco_drag_add_beeps(uint8_t beeps);
+ao_lco_drag_add_beeps(int8_t beeps);
 
 /* task function for beeping while arm is active */
 void
@@ -130,10 +209,10 @@ ao_lco_arm_warn(void);
  */
 
 void
-ao_lco_show_pad(uint8_t pad);
+ao_lco_show_pad(int8_t pad);
 
 void
-ao_lco_show_box(uint16_t box);
+ao_lco_show_box(int16_t box);
 
 void
 ao_lco_show(void);
@@ -142,6 +221,38 @@ void
 ao_lco_init(void);
 
 uint8_t
-ao_lco_box_present(uint16_t box);
+ao_lco_box_present(int16_t box);
+
+#ifdef AO_LCO_HAS_CONTRAST
+void
+ao_lco_set_contrast(int32_t contrast);
+
+int32_t
+ao_lco_get_contrast(void);
+#endif
+
+#ifdef AO_LCO_HAS_BACKLIGHT
+void
+ao_lco_set_backlight(int32_t backlight);
+
+int32_t
+ao_lco_get_backlight(void);
+#endif
+
+#ifdef AO_LCO_SEARCH_API
+
+void
+ao_lco_search_start(void);
+
+void
+ao_lco_search_box_check(int16_t box);
+
+void
+ao_lco_search_box_present(int16_t box);
+
+void
+ao_lco_search_done(void);
+
+#endif /* AO_LCO_SEARCH_API */
 
 #endif /* _AO_LCO_H_ */
index acdea87e3328759d8b292864ee02e28be3497e64..ebe3d0cb2989b12ca59ca15fefd4ab2081a9dea7 100644 (file)
 
 uint8_t                ao_lco_debug;
 
-uint8_t                ao_lco_pad;
-uint16_t       ao_lco_box;
+int8_t         ao_lco_pad;
+int16_t                ao_lco_box;
 
 uint8_t                ao_lco_armed;                                   /* arm active */
 uint8_t                ao_lco_firing;                                  /* fire active */
 
-uint16_t       ao_lco_min_box, ao_lco_max_box;
+int16_t                ao_lco_min_box, ao_lco_max_box;
 
 uint8_t                ao_lco_pretending;
 
@@ -38,10 +38,7 @@ static uint8_t               ao_lco_channels[AO_PAD_MAX_BOXES];      /* pad channels available on
 static uint16_t                ao_lco_tick_offset[AO_PAD_MAX_BOXES];   /* 16 bit offset from local to remote tick count */
 static uint8_t         ao_lco_selected[AO_PAD_MAX_BOXES];      /* pads selected to fire */
 
-#define AO_LCO_VALID_LAST      1
-#define AO_LCO_VALID_EVER      2
-
-static uint8_t         ao_lco_valid[AO_PAD_MAX_BOXES];         /* AO_LCO_VALID bits per box */
+uint8_t                ao_lco_valid[AO_PAD_MAX_BOXES];         /* AO_LCO_VALID bits per box */
 
 static const AO_LED_TYPE       continuity_led[AO_LED_CONTINUITY_NUM] = {
 #ifdef AO_LED_CONTINUITY_0
@@ -84,6 +81,10 @@ ao_lco_igniter_status(void)
                else
 #endif
                        ao_sleep(&ao_pad_query);
+               if (ao_lco_box_pseudo(ao_lco_box)) {
+                       ao_led_off(AO_LED_GREEN|AO_LED_AMBER|AO_LED_RED);
+                       continue;
+               }
                PRINTD("RSSI %d VALID %d\n", ao_radio_cmac_rssi, ao_lco_valid[ao_lco_box]);
                if (!(ao_lco_valid[ao_lco_box] & AO_LCO_VALID_LAST)) {
                        ao_led_on(AO_LED_RED);
@@ -138,10 +139,10 @@ ao_lco_igniter_status(void)
 }
 
 uint8_t
-ao_lco_pad_present(uint16_t box, uint8_t pad)
+ao_lco_pad_present(int16_t box, int8_t pad)
 {
        /* voltage measurement is always valid */
-       if (pad == AO_LCO_PAD_VOLTAGE)
+       if (ao_lco_pad_pseudo(pad))
                return 1;
        if (!ao_lco_channels[box])
                return 0;
@@ -150,10 +151,10 @@ ao_lco_pad_present(uint16_t box, uint8_t pad)
        return (ao_lco_channels[box] >> (pad - 1)) & 1;
 }
 
-uint8_t
-ao_lco_pad_first(uint16_t box)
+int8_t
+ao_lco_pad_first(int16_t box)
 {
-       uint8_t pad;
+       int8_t  pad;
 
        for (pad = 1; pad <= AO_PAD_MAX_CHANNELS; pad++)
                if (ao_lco_pad_present(box, pad))
@@ -162,11 +163,11 @@ ao_lco_pad_first(uint16_t box)
 }
 
 static uint8_t
-ao_lco_get_channels(uint16_t box, struct ao_pad_query *query)
+ao_lco_get_channels(int16_t box, struct ao_pad_query *query)
 {
        int8_t                  r;
 
-       r = ao_lco_query(box, query, &ao_lco_tick_offset[box]);
+       r = ao_lco_query((uint16_t) box, query, &ao_lco_tick_offset[box]);
        if (r == AO_RADIO_CMAC_OK) {
                ao_lco_channels[box] = query->channels;
                ao_lco_valid[box] = AO_LCO_VALID_LAST | AO_LCO_VALID_EVER;
@@ -180,14 +181,19 @@ ao_lco_get_channels(uint16_t box, struct ao_pad_query *query)
 void
 ao_lco_update(void)
 {
+       if (ao_lco_box_pseudo(ao_lco_box)) {
+               ao_lco_show();
+               return;
+       }
+
        uint8_t previous_valid = ao_lco_valid[ao_lco_box];
 
        if (ao_lco_get_channels(ao_lco_box, &ao_pad_query) & AO_LCO_VALID_LAST) {
                if (!(previous_valid & AO_LCO_VALID_EVER)) {
-                       if (ao_lco_pad != AO_LCO_PAD_VOLTAGE)
+                       if (!ao_lco_pad_pseudo(ao_lco_pad))
                                ao_lco_set_pad(ao_lco_pad_first(ao_lco_box));
                }
-               if (ao_lco_pad == AO_LCO_PAD_VOLTAGE)
+               if (ao_lco_pad_pseudo(ao_lco_pad))
                        ao_lco_show();
        }
 }
@@ -204,7 +210,7 @@ ao_lco_box_reset_present(void)
 }
 
 static void
-ao_lco_box_set_present(uint16_t box)
+ao_lco_box_set_present(int16_t box)
 {
        if (box < ao_lco_min_box)
                ao_lco_min_box = box;
@@ -216,21 +222,23 @@ ao_lco_box_set_present(uint16_t box)
 }
 
 void
-ao_lco_set_pad(uint8_t new_pad)
+ao_lco_set_pad(int8_t new_pad)
 {
        ao_lco_pad = new_pad;
        ao_lco_show();
 }
 
 void
-ao_lco_set_box(uint16_t new_box)
+ao_lco_set_box(int16_t new_box)
 {
        ao_lco_box = new_box;
-       if (ao_lco_box < AO_PAD_MAX_BOXES) {
-               if (ao_lco_pretending)
-                       ao_lco_channels[ao_lco_box] = 0xff;
-               else
-                       ao_lco_channels[ao_lco_box] = 0;
+       if (!ao_lco_box_pseudo(ao_lco_box)) {
+               if (ao_lco_box < AO_PAD_MAX_BOXES) {
+                       if (ao_lco_pretending)
+                               ao_lco_channels[ao_lco_box] = 0xff;
+                       else
+                               ao_lco_channels[ao_lco_box] = 0;
+               }
        }
        ao_lco_pad = 1;
        ao_lco_show();
@@ -241,28 +249,111 @@ ao_lco_step_pad(int8_t dir)
 {
        int16_t new_pad;
 
-       new_pad = (int16_t) ao_lco_pad;
+       switch (ao_lco_box) {
+#ifdef AO_LCO_HAS_CONTRAST
+       case AO_LCO_CONTRAST: {
+               int32_t contrast = ao_lco_get_contrast();
+
+               contrast = (contrast + AO_LCO_CONTRAST_STEP - 1) / AO_LCO_CONTRAST_STEP;
+               contrast += dir;
+               contrast *= AO_LCO_CONTRAST_STEP;
+               if (contrast < AO_LCO_MIN_CONTRAST)
+                       contrast = AO_LCO_MIN_CONTRAST;
+               if (contrast > AO_LCO_MAX_CONTRAST)
+                       contrast = AO_LCO_MAX_CONTRAST;
+               ao_lco_set_contrast(contrast);
+               ao_lco_show();
+               break;
+       }
+#endif
+#ifdef AO_LCO_HAS_BACKLIGHT
+       case AO_LCO_BACKLIGHT: {
+               int32_t backlight = ao_lco_get_backlight();
+
+               backlight = (backlight + AO_LCO_BACKLIGHT_STEP - 1) / AO_LCO_BACKLIGHT_STEP;
+               backlight += dir;
+               backlight *= AO_LCO_BACKLIGHT_STEP;
+               if (backlight < AO_LCO_MIN_BACKLIGHT)
+                       backlight = AO_LCO_MIN_BACKLIGHT;
+               if (backlight > AO_LCO_MAX_BACKLIGHT)
+                       backlight = AO_LCO_MAX_BACKLIGHT;
+               ao_lco_set_backlight(backlight);
+               ao_lco_show();
+               break;
+       }
+#endif
+#ifdef AO_LCO_HAS_INFO
+       case AO_LCO_INFO: {
+#if AO_LCO_MIN_INFO_PAGE < AO_LCO_MAX_INFO_PAGE
+               int32_t info_page = ao_lco_get_info_page();
+
+               info += dir;
+               if (info_page < AO_LCO_MIN_INFO_PAGE)
+                       info_page = AO_LCO_MIN_INFO_PAGE;
+               if (info_page > AO_LCO_MAX_INFO_PAGE)
+                       info_page = AO_LCO_MAX_INFO_PAGE;
+               ao_lco_set_info_page();
+#endif
+               break;
+       }
+#endif
+       default:
+               new_pad = (int16_t) ao_lco_pad;
+               do {
+                       new_pad += dir;
+                       if (new_pad > AO_PAD_MAX_CHANNELS)
+                               new_pad = AO_LCO_PAD_FIRST;
+                       if (new_pad < AO_LCO_PAD_FIRST)
+                               new_pad = AO_PAD_MAX_CHANNELS;
+                       if (new_pad == ao_lco_pad)
+                               break;
+               } while (!ao_lco_pad_present(ao_lco_box, (int8_t) new_pad));
+               PRINTD("New pad %d\n", new_pad);
+               ao_lco_set_pad((int8_t) new_pad);
+               break;
+       }
+}
+
+uint8_t
+ao_lco_box_present(int16_t box)
+{
+       if (ao_lco_box_pseudo(box))
+               return 1;
+       if (box >= AO_PAD_MAX_BOXES)
+               return 0;
+       return (ao_lco_box_mask[AO_LCO_MASK_ID(box)] >> AO_LCO_MASK_SHIFT(box)) & 1;
+}
+
+void
+ao_lco_step_box(int8_t dir)
+{
+       int16_t new_box = ao_lco_box;
+
        do {
-               new_pad += dir;
-               if (new_pad > AO_PAD_MAX_CHANNELS)
-                       new_pad = AO_LCO_PAD_VOLTAGE;
-               if (new_pad < 0)
-                       new_pad = AO_PAD_MAX_CHANNELS;
-               if (new_pad == ao_lco_pad)
+               new_box += dir;
+               if (new_box > ao_lco_max_box)
+                       new_box = AO_LCO_BOX_FIRST;
+               else if (new_box < AO_LCO_BOX_FIRST)
+                       new_box = ao_lco_max_box;
+               if (new_box == ao_lco_box)
                        break;
-       } while (!ao_lco_pad_present(ao_lco_box, (uint8_t) new_pad));
-       ao_lco_set_pad((uint8_t) new_pad);
+       } while (!ao_lco_box_present(new_box));
+       PRINTD("New box %d\n", new_box);
+       ao_lco_set_box(new_box);
 }
 
 void
 ao_lco_set_armed(uint8_t armed)
 {
+       if (ao_lco_box_pseudo(ao_lco_box))
+               return;
+
        ao_lco_armed = armed;
        PRINTD("Armed %d\n", ao_lco_armed);
        if (ao_lco_armed) {
 #if AO_LCO_DRAG
                if (ao_lco_drag_race) {
-                       uint16_t        box;
+                       int16_t box;
 
                        for (box = ao_lco_min_box; box <= ao_lco_max_box; box++)
                                if (ao_lco_selected[box])
@@ -290,28 +381,60 @@ ao_lco_set_firing(uint8_t firing)
        ao_wakeup(&ao_lco_armed);
 }
 
+#if 0
+static int16_t fake_boxes[] = {
+       1, 2, 3, 5, 8, 11, 13, 17, 19, 23, 29, 31, 37, 62, 97
+};
+#define ARRAYSIZE(a)   (sizeof(a) / sizeof((a)[0]))
+#define NFAKE ARRAYSIZE(fake_boxes)
+
+static bool
+is_fake(int16_t box)
+{
+       unsigned i;
+       for (i = 0; i < NFAKE; i++)
+               if (fake_boxes[i] == box)
+                       return true;
+       return false;
+}
+#else
+#define is_fake(b)     false
+#endif
+
 void
 ao_lco_search(void)
 {
        int8_t          r;
        int8_t          try;
-       uint16_t        box;
+       int16_t         box;
        uint16_t        boxes = 0;
 
        ao_lco_box_reset_present();
+#ifdef AO_LCO_SEARCH_API
+       ao_lco_search_start();
+#else
        ao_lco_show_box(0);
        ao_lco_show_pad(0);
+#endif
        for (box = 0; box < AO_PAD_MAX_BOXES; box++) {
+#ifdef AO_LCO_SEARCH_API
+               ao_lco_search_box_check(box);
+#else
                if ((box % 10) == 0)
                        ao_lco_show_box(box);
+#endif
                for (try = 0; try < 3; try++) {
                        ao_lco_tick_offset[box] = 0;
-                       r = ao_lco_query(box, &ao_pad_query, &ao_lco_tick_offset[box]);
+                       r = ao_lco_query((uint16_t) box, &ao_pad_query, &ao_lco_tick_offset[box]);
                        PRINTD("box %d result %d offset %d\n", box, r, ao_lco_tick_offset[box]);
-                       if (r == AO_RADIO_CMAC_OK) {
+                       if (r == AO_RADIO_CMAC_OK || is_fake(box)) {
                                ++boxes;
                                ao_lco_box_set_present(box);
-                               ao_lco_show_pad((uint8_t) (boxes % 10));
+#ifdef AO_LCO_SEARCH_API
+                               ao_lco_search_box_present(box);
+#else
+                               ao_lco_show_pad((int8_t) (boxes % 10));
+#endif
                                ao_delay(AO_MS_TO_TICKS(30));
                                break;
                        }
@@ -323,18 +446,21 @@ ao_lco_search(void)
                ao_lco_min_box = ao_lco_max_box = ao_lco_box = 0;
        memset(ao_lco_valid, 0, sizeof (ao_lco_valid));
        memset(ao_lco_channels, 0, sizeof (ao_lco_channels));
+#ifdef AO_LCO_SEARCH_API
+       ao_lco_search_done();
+#endif
        ao_lco_set_box(ao_lco_min_box);
 }
 
 void
 ao_lco_pretend(void)
 {
-       uint16_t box;
+       int16_t box;
 
        ao_lco_pretending = 1;
        ao_lco_min_box = 1;
        ao_lco_max_box = AO_PAD_MAX_BOXES - 1;
-       for (box = ao_lco_min_box; box < ao_lco_max_box; box++)
+       for (box = ao_lco_min_box; box <= ao_lco_max_box; box++)
                ao_lco_box_set_present(box);
        ao_lco_box = ao_lco_min_box;
        memset(ao_lco_valid, 0, sizeof (ao_lco_valid));
@@ -345,8 +471,8 @@ ao_lco_pretend(void)
 void
 ao_lco_monitor(void)
 {
-       AO_TICK_TYPE            delay;
-       uint16_t                box;
+       AO_TICK_TYPE    delay;
+       int16_t         box;
 
        for (;;) {
                PRINTD("monitor armed %d firing %d\n",
@@ -362,7 +488,7 @@ ao_lco_monitor(void)
                                                PRINTD("Arming box %d pads %x\n",
                                                       box, ao_lco_selected[box]);
                                                if (ao_lco_valid[box] & AO_LCO_VALID_EVER) {
-                                                       ao_lco_arm(box, ao_lco_selected[box], ao_lco_tick_offset[box]);
+                                                       ao_lco_arm((uint16_t) box, ao_lco_selected[box], ao_lco_tick_offset[box]);
                                                        ao_delay(AO_MS_TO_TICKS(10));
                                                }
                                        }
@@ -379,7 +505,7 @@ ao_lco_monitor(void)
 
 #if AO_LCO_DRAG
 
-uint8_t                        ao_lco_drag_beep_count;
+int8_t                 ao_lco_drag_beep_count;
 static uint8_t         ao_lco_drag_beep_on;
 static AO_TICK_TYPE    ao_lco_drag_beep_time;
 static AO_TICK_TYPE    ao_lco_drag_warn_time;
@@ -389,7 +515,7 @@ static AO_TICK_TYPE ao_lco_drag_warn_time;
 
 /* Request 'beeps' additional drag race beeps */
 void
-ao_lco_drag_add_beeps(uint8_t beeps)
+ao_lco_drag_add_beeps(int8_t beeps)
 {
        PRINTD("beep %d\n", beeps);
        if (ao_lco_drag_beep_count == 0)
@@ -402,7 +528,7 @@ ao_lco_drag_add_beeps(uint8_t beeps)
 void
 ao_lco_toggle_drag(void)
 {
-       if (ao_lco_drag_race && ao_lco_pad != AO_LCO_PAD_VOLTAGE) {
+       if (ao_lco_drag_race && !ao_lco_pad_pseudo(ao_lco_pad) && !ao_lco_box_pseudo(ao_lco_box)) {
                ao_lco_selected[ao_lco_box] ^= (uint8_t) (1 << (ao_lco_pad - 1));
                PRINTD("Toggle box %d pad %d (pads now %x) to drag race\n",
                       ao_lco_pad, ao_lco_box, ao_lco_selected[ao_lco_box]);
index 0eb90080acb76dae8d288b8fe466d6d39b06ab32..57c006b78eba4571933901c9582d30609bfd4a5b 100644 (file)
@@ -46,13 +46,13 @@ ao_lco_wakeup(void)
 }
 
 void
-ao_lco_show_pad(uint8_t pad)
+ao_lco_show_pad(int8_t pad)
 {
        (void) pad;
 }
 
 void
-ao_lco_show_box(uint16_t box)
+ao_lco_show_box(int16_t box)
 {
        (void) box;
 }
@@ -85,7 +85,7 @@ ao_lco_input(void)
                case AO_EVENT_BUTTON:
                        switch (event.unit) {
                        case AO_BUTTON_BOX:
-                               ao_lco_set_box((uint16_t) event.value);
+                               ao_lco_set_box((int16_t) event.value);
                                ao_lco_set_armed(0);
                                break;
                        case AO_BUTTON_ARM:
index accf943a3fbe6e079ad0825b21f53d08823828fb..4be91e13b7599cedd73f7778134d7187d51a7e1d 100644 (file)
@@ -101,7 +101,7 @@ static uint8_t ao_m25_mutex;
 
 static uint8_t ao_m25_instruction[4];
 
-#define AO_M25_SPI_SPEED       ao_spi_speed(10000000)  /* this seems like a reasonable minimum speed to require */
+#define AO_M25_SPI_SPEED       ao_spi_speed(AO_M25_SPI_BUS, 10000000)  /* this seems like a reasonable minimum speed to require */
 
 #define M25_SELECT(cs)         ao_spi_get_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS,AO_M25_SPI_SPEED)
 #define M25_DESELECT(cs)       ao_spi_put_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS)
index 3fd8e064602bc092f00319c2a3878c3b65f0fc80..f79ea2d75f24ac445911a3a80db58dad55225398 100644 (file)
@@ -30,7 +30,7 @@
 #define PRINTD(l,...) 
 #endif
 
-#define AO_MMA655X_SPI_SPEED   ao_spi_speed(8333333)   /* 120ns clock period */
+#define AO_MMA655X_SPI_SPEED   ao_spi_speed(AO_MMA655X_SPI_INDEX, 8333333)     /* 120ns clock period */
 
 static void
 ao_mma655x_start(void) {
index 73e30d0390bde6e3cb8fc82b419c5a456d2b3fa8..02df7a2661e82149403f16215db53101c0c2ef3b 100644 (file)
@@ -30,6 +30,60 @@ static struct ao_mmc5983_sample      ao_mmc5983_offset;
 static uint8_t ao_mmc5983_configured;
 
 #ifdef MMC5983_I2C
+#ifdef AO_MMC5983_I2C_INDEX
+
+static void
+ao_mmc5983_start(void) {
+       ao_i2c_get(AO_MMC5983_I2C_INDEX);
+}
+
+static void
+ao_mmc5983_stop(void) {
+       ao_i2c_put(AO_MMC5983_I2C_INDEX);
+}
+
+static void
+ao_mmc5983_reg_write(uint8_t addr, uint8_t data)
+{
+       uint8_t d[2];
+
+       d[0] = addr;
+       d[1] = data;
+
+       ao_mmc5983_start();
+       ao_i2c_start(AO_MMC5983_I2C_INDEX, MMC5983_I2C_ADDR);
+       ao_i2c_send(d, 2, AO_MMC5983_I2C_INDEX, true);
+       ao_mmc5983_stop();
+}
+
+static uint8_t
+ao_mmc5983_reg_read(uint8_t addr)
+{
+       uint8_t d[1];
+
+       ao_mmc5983_start();
+       ao_i2c_start(AO_MMC5983_I2C_INDEX, MMC5983_I2C_ADDR);
+       d[0] = addr;
+       ao_i2c_send(d, 1, AO_MMC5983_I2C_INDEX, false);
+       ao_i2c_start(AO_MMC5983_I2C_INDEX, MMC5983_I2C_ADDR | 1);
+       ao_i2c_recv(d, 1, AO_MMC5983_I2C_INDEX, true);
+       ao_mmc5983_stop();
+       return d[0];
+}
+
+static void
+ao_mmc5983_raw(struct ao_mmc5983_raw *raw)
+{
+       ao_mmc5983_start();
+       ao_i2c_start(AO_MMC5983_I2C_INDEX, MMC5983_I2C_ADDR);
+       raw->addr = MMC5983_X_OUT_0;
+       ao_i2c_send(&(raw->addr), 1, AO_MMC5983_I2C_INDEX, false);
+       ao_i2c_start(AO_MMC5983_I2C_INDEX, MMC5983_I2C_ADDR | 1);
+       ao_i2c_recv(&(raw->x0), sizeof(*raw) - 1, AO_MMC5983_I2C_INDEX, true);
+       ao_mmc5983_stop();
+}
+
+#else
 #include <ao_i2c_bit.h>
 
 static void
@@ -70,8 +124,10 @@ ao_mmc5983_raw(struct ao_mmc5983_raw *raw)
        ao_i2c_bit_stop();
 }
 
+#endif
+
 #else
-#define AO_MMC5983_SPI_SPEED   ao_spi_speed(2000000)
+#define AO_MMC5983_SPI_SPEED   ao_spi_speed(AO_MMC5983_SPI_INDEX, 2000000)
 
 static void
 ao_mmc5983_start(void) {
@@ -356,9 +412,7 @@ ao_mmc5983_init(void)
 {
        ao_mmc5983_configured = 0;
 
-#ifdef MMC5983_I2C
-       ao_enable_output(AO_MMC5983_SPI_CS_PORT, AO_MMC5983_SPI_CS_PIN, 1);
-#else
+#ifndef MMC5983_I2C
        ao_enable_input(AO_MMC5983_SPI_MISO_PORT,
                        AO_MMC5983_SPI_MISO_PIN,
                        AO_EXTI_MODE_PULL_NONE);
index c33032af7e5dc017e363b19171a585eb62e0f76a..e5594a662c6dfcb62f6d11e0acd1a78565bd2ce1 100644 (file)
@@ -32,7 +32,7 @@ static uint8_t        ao_mpu6000_configured;
 
 #if AO_MPU6000_SPI
 
-#define AO_MPU6000_SPI_SPEED   ao_spi_speed(1000000)   /* 1Mhz for all register access */
+#define AO_MPU6000_SPI_SPEED   ao_spi_speed(AO_MPU6000_SPI_BUS, 1000000)       /* 1Mhz for all register access */
 
 #define ao_mpu6000_spi_get()   ao_spi_get(AO_MPU6000_SPI_BUS, AO_MPU6000_SPI_SPEED)
 #define ao_mpu6000_spi_put()   ao_spi_put(AO_MPU6000_SPI_BUS)
index a77abf98dfe7bcfa2d6ce3b8267b1571dd7d642b..63d8d6d7fe31ffbc32dc8c786dc349df9bfee413 100644 (file)
@@ -35,7 +35,7 @@ static uint8_t        ao_mpu9250_configured;
 #if AO_MPU9250_SPI
 
 #ifndef AO_MPU9250_SPI_SPEED
-#define AO_MPU9250_SPI_SPEED   ao_spi_speed(1000000)   /* 1MHz max SCLK */
+#define AO_MPU9250_SPI_SPEED   ao_spi_speed(AO_MPU9250_SPI_BUS, 1000000)       /* 1MHz max SCLK */
 #endif
 
 #define ao_mpu9250_spi_get()   ao_spi_get(AO_MPU9250_SPI_BUS, AO_MPU9250_SPI_SPEED)
index dce7c0e6a3c5a5de8af7ccf570ee6b43c0c5f9b9..4a9802d7190d4c06eaeeb6e128af96f5e3daceb8 100644 (file)
@@ -25,7 +25,7 @@
 struct ao_ms5607_prom  ao_ms5607_prom;
 static uint8_t         ms5607_configured;
 
-#define AO_MS5607_SPI_SPEED    ao_spi_speed(20000000)
+#define AO_MS5607_SPI_SPEED    ao_spi_speed(AO_MS5607_SPI_INDEX, 20000000)
 
 static void
 ao_ms5607_start(void) {
index 1be305e57e16ac5955bedca84ad634edeb731f4d..d468a5486ef1b54da142da5966a6daf5d15aaeca 100644 (file)
@@ -209,8 +209,8 @@ static void
 ao_seven_segment_show(void)
 {
        uint8_t digit, value;
-       digit = ao_cmd_decimal();
-       value = ao_cmd_decimal();
+       digit = (uint8_t) ao_cmd_decimal();
+       value = (uint8_t) ao_cmd_decimal();
        ao_seven_segment_set(digit, value);
 }
 
diff --git a/src/drivers/ao_st7565.c b/src/drivers/ao_st7565.c
new file mode 100644 (file)
index 0000000..3eec610
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright © 2023 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_st7565.h>
+
+static void
+ao_st7565_reset(void)
+{
+       ao_gpio_set(AO_ST7565_RESET_PORT, AO_ST7565_RESET_PIN, 0);
+       ao_delay(AO_MS_TO_TICKS(100));
+       ao_gpio_set(AO_ST7565_RESET_PORT, AO_ST7565_RESET_PIN, 1);
+       ao_delay(AO_MS_TO_TICKS(100));
+}
+
+
+static void
+ao_st7565_start(uint8_t a0)
+{
+       ao_gpio_set(AO_ST7565_A0_PORT, AO_ST7565_A0_PIN, a0);
+       ao_spi_get_bit(AO_ST7565_CS_PORT,
+                      AO_ST7565_CS_PIN,
+                      AO_ST7565_SPI_BUS,
+                      AO_ST7565_SPI_SPEED);
+}
+
+static void
+ao_st7565_stop(void)
+{
+       ao_spi_put_bit(AO_ST7565_CS_PORT,
+                      AO_ST7565_CS_PIN,
+                      AO_ST7565_SPI_BUS);
+       ao_gpio_set(AO_ST7565_A0_PORT, AO_ST7565_A0_PIN, 1);
+}
+
+
+static void
+ao_st7565_instruction(uint8_t cmd)
+{
+       ao_st7565_start(0);
+       ao_spi_send(&cmd, 1, AO_ST7565_SPI_BUS);
+       ao_st7565_stop();
+}
+
+static void
+ao_st7565_instruction_param(uint8_t cmd, uint8_t param)
+{
+       uint8_t b[2] = { cmd, param };
+       ao_st7565_start(0);
+       ao_spi_send(b, 2, AO_ST7565_SPI_BUS);
+       ao_st7565_stop();
+}
+
+static void
+ao_st7565_instructions(const uint8_t *cmd, uint16_t len)
+{
+       ao_st7565_start(0);
+       ao_spi_send(cmd, len, AO_ST7565_SPI_BUS);
+       ao_st7565_stop();
+}
+
+static void
+ao_st7565_data(const void *base, uint16_t len)
+{
+       ao_st7565_start(1);
+       ao_spi_send(base, len, AO_ST7565_SPI_BUS);
+       ao_st7565_stop();
+}
+
+static uint8_t brightness;
+
+void
+ao_st7565_set_brightness(uint8_t val)
+{
+       if (val > 63)
+               val = 63;
+       brightness = val;
+       ao_st7565_instruction_param(ST7565_ELECTRONIC_VOLUME_SET, val);
+}
+
+uint8_t
+ao_st7565_get_brightness(void)
+{
+       return brightness;
+}
+
+static bool setup_done;
+
+static void
+ao_st7565_setup(void)
+{
+       static const uint8_t init[] = {
+               /*
+                * Should be set to one of ST7565_LCD_BIAS_1_9 or
+                * ST7565_LCD_BIAS_1_7
+                */
+               AO_ST7565_BIAS,
+               ST7565_ADC_SELECT_NORMAL,
+               ST7565_COMMON_MODE_NORMAL,
+               ST7565_DISPLAY_START_LINE_SET(0),
+               ST7565_POWER_CONTROL_SET(0x4),
+       };
+
+       if (setup_done)
+               return;
+       setup_done = true;
+       ao_st7565_reset();
+       ao_st7565_instructions(init, sizeof(init));
+       ao_delay(AO_MS_TO_TICKS(50));
+       ao_st7565_instruction(ST7565_POWER_CONTROL_SET(0x6));
+       ao_delay(AO_MS_TO_TICKS(50));
+       ao_st7565_instruction(ST7565_POWER_CONTROL_SET(0x7));
+       ao_delay(AO_MS_TO_TICKS(10));
+       ao_st7565_instruction(ST7565_RESISTOR_RATIO_SET(5));
+       ao_st7565_instruction(ST7565_DISPLAY_ON);
+       ao_st7565_set_brightness(13);
+}
+
+static uint8_t rotbuf[AO_ST7565_WIDTH];
+
+#define WIDTH  AO_ST7565_WIDTH
+#define HEIGHT AO_ST7565_HEIGHT
+#define STRIDE AO_BITMAP_STRIDE(WIDTH)
+
+static uint32_t        previous_image[STRIDE * HEIGHT];
+
+void
+ao_st7565_update(struct ao_bitmap *bitmap)
+{
+       int16_t         col, c, page;
+       int16_t         row;
+       uint32_t        *line, *prev, *l;
+       uint32_t        bits;
+       uint8_t         *r;
+       int16_t         min_col, min_row, max_col, max_row;
+       int16_t         min_page, max_page;
+
+       ao_st7565_setup();
+
+       min_col = STRIDE - 1;
+       max_col = 0;
+       min_row = HEIGHT - 1;
+       max_row = 0;
+       line = bitmap->base;
+       prev = previous_image;
+       for (row = 0; row < HEIGHT; row++) {
+               for (col = 0; col < STRIDE; col++) {
+                       bits = *line++;
+                       if (bits != *prev) {
+                               *prev = bits;
+                               if (row < min_row)
+                                       min_row = row;
+                               if (row > max_row)
+                                       max_row = row;
+                               if (col < min_col)
+                                       min_col = col;
+                               if (col > max_col)
+                                       max_col = col;
+                       }
+                       prev++;
+               }
+       }
+
+       if (min_col > max_col || min_row > max_row)
+               return;
+
+       min_page = min_row >> 3;
+       max_page = max_row >> 3;
+       line = bitmap->base + min_page * 8 * STRIDE + min_col;
+
+       uint8_t first_col = (uint8_t) (min_col * 32);
+       uint8_t num_col = (uint8_t) (max_col + 1 - min_col) * 32;
+
+       for (page = min_page; page <= max_page; page++) {
+               uint8_t         i[4] = {
+                       ST7565_PAGE_ADDRESS_SET(7-(uint8_t) page),
+                       ST7565_COLUMN_ADDRESS_SET_MSN(first_col >> 4),
+                       ST7565_COLUMN_ADDRESS_SET_LSN(first_col & 0xf),
+                       ST7565_RMW
+               };
+               memset(rotbuf, 0, num_col);
+               for (row = 7; row >= 0; row--) {
+                       r = rotbuf;
+                       l = line;
+                       line += STRIDE;
+                       for (col = min_col; col <= max_col; col++) {
+                               bits = ~*l++;
+                               for (c = 0; c < 32; c++) {
+                                       *r++ |= ((bits >> c) & 1) << row;
+                               }
+                       }
+               }
+               ao_st7565_instructions(i, 4);
+               ao_st7565_data(rotbuf, num_col);
+       }
+}
+
+void
+ao_st7565_init(void)
+{
+       memset(previous_image, 0xff, sizeof(previous_image));
+       ao_enable_output(AO_ST7565_RESET_PORT, AO_ST7565_RESET_PIN, 1);
+       ao_enable_output(AO_ST7565_A0_PORT, AO_ST7565_A0_PIN, 1);
+
+       ao_enable_cs(AO_ST7565_CS_PORT, AO_ST7565_CS_PIN);
+}
diff --git a/src/drivers/ao_st7565.h b/src/drivers/ao_st7565.h
new file mode 100644 (file)
index 0000000..b5627f3
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright © 2023 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_ST7565_H_
+#define _AO_ST7565_H_
+
+#include <ao_draw.h>
+
+/*
+ * Wiring the NewHaven NHD-C12864LZ-FSW-FBW-3V3 display
+ *
+ * PIN Symbol  Connection
+ *  1  /CS1    Chip select. Active low.
+ *  2  /RES    Reset. Active low.
+ *  3  A0      Register select. 0: instruction, 1: data
+ *  4  /WR     NC
+ *  5  /RD     NC
+ *  6  DB0     NC
+ *  7  DB1     NC
+ *  8  DB2     NC
+ *  8  DB3     NC
+ *  10 DB4     NC
+ *  11 DB5     NC
+ *  12 DB6     SPI clock. Mode 3 (idle high, rising edge)
+ *  13 DB7     SPI data.
+ *  14 Vdd     +3.3V
+ *  15 Vss     Ground
+ *  16 Vout    1uF to ground
+ *  17  CAP3+  1uF to CAP1-
+ *  18 CAP1-   1uF to CAP3+ and CAP1+
+ *  19 CAP1+   1uF to CAP1-
+ *  20 CAP2+   1uF to CAP2-
+ *  21 CAP2-   1uF to CAP2+
+ *  22 V4      1uF to ground
+ *  23 V3      1uF to ground
+ *  24 V2      1uF to ground
+ *  25 V1      1uF to ground
+ *  26 V0      1uF to ground
+ *  27 C86     MPU select. Ground
+ *  28 PS      Parallel/serial select. Ground
+ */
+
+#define ST7565_DISPLAY_OFF                     0xae
+#define ST7565_DISPLAY_ON                      0xaf
+#define ST7565_DISPLAY_START_LINE_SET(line)    (0x40 | (line))
+#define ST7565_PAGE_ADDRESS_SET(page)          (0xb0 | (page))
+#define ST7565_COLUMN_ADDRESS_SET_MSN(nib)     (0x10 | (nib))
+#define ST7565_COLUMN_ADDRESS_SET_LSN(nib)     (0x00 | (nib))
+#define ST7565_ADC_SELECT_NORMAL               0xa0
+#define ST7565_ADC_SELECT_REVERSE              0xa1
+#define ST7565_DISPLAY_NORMAL                  0xa6
+#define ST7565_DISPLAY_REVERSE                 0xa7
+#define ST7565_DISPLAY_ALL_POINTS_OFF          0xa4
+#define ST7565_DISPLAY_ALL_POINTS_ON           0xa5
+#define ST7565_LCD_BIAS_1_9                    0xa2
+#define ST7565_LCD_BIAS_1_7                    0xa3
+#define ST7565_RMW                             0xe0
+#define ST7565_RMW_END                         0xee
+#define ST7565_RESET                           0xe2
+#define ST7565_COMMON_MODE_NORMAL              0xc0
+#define ST7565_COMMON_MODE_REVERSE             0xc8
+#define ST7565_POWER_CONTROL_SET(pc)           (0x28 | (pc))
+#define ST7565_RESISTOR_RATIO_SET(rr)          (0x20 | (rr))
+#define ST7565_ELECTRONIC_VOLUME_SET           0x81
+#define ST7565_SLEEP_MODE                      0xac
+#define ST7565_BOOSTER_RATIO_SET               0xf8
+#define ST7565_NOP                             0xe3
+
+/* Max 50ns SPI clock time */
+#define AO_ST7565_SPI_SPEED                    ao_spi_speed(AO_ST7565_SPI_BUS, 20000000)
+
+void
+ao_st7565_update(struct ao_bitmap *bitmap);
+
+void
+ao_st7565_set_brightness(uint8_t val);
+
+uint8_t
+ao_st7565_get_brightness(void);
+
+void
+ao_st7565_init(void);
+
+#endif /* _AO_ST7565_H_ */
index 51c517558fff12442fae7b3853b03c8e46141fe9..c145b83b2a8557990416a6e64393e4c0fde40a56 100644 (file)
@@ -3,7 +3,7 @@
 #
 #
 
-include ../stm/Makefile.defs
+include ../stm32f1/Makefile.defs
 
 INC = \
        ao.h \
@@ -20,38 +20,27 @@ INC = \
        ao_ms5607.h \
        ao_bmx160.h \
        ao_adxl375.h \
-       ao_profile.h \
        ao_task.h \
        ao_whiten.h \
        ao_sample_profile.h \
        ao_quaternion.h \
-       ao_mpu.h \
-       stm32l.h \
+       stm32f1.h \
        Makefile
 
 #
 # Common AltOS sources
 #
 
-#PROFILE=ao_profile.c
-#PROFILE_DEF=-DAO_PROFILE=1
-
-#SAMPLE_PROFILE=ao_sample_profile.c \
-#      ao_sample_profile_timer.c
-#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1
-
-#STACK_GUARD=ao_mpu_stm.c
-#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
-
 ALTOS_SRC = \
        ao_boot_chain.c \
        ao_interrupt.c \
+       ao_clock.c \
        ao_product.c \
        ao_romconfig.c \
        ao_cmd.c \
        ao_config.c \
        ao_task.c \
-       ao_led_stm.c \
+       ao_led.c \
        ao_stdio.c \
        ao_panic.c \
        ao_timer.c \
@@ -60,19 +49,19 @@ ALTOS_SRC = \
        ao_freq.c \
        ao_dma_stm.c \
        ao_spi_stm.c \
+       ao_i2c_stm.c \
        ao_data.c \
        ao_ms5607.c \
-       ao_bmx160.c \
+       ao_bmi088.c \
+       ao_mmc5983.c \
        ao_adxl375.c \
        ao_adc_stm.c \
        ao_beep_stm.c \
-       ao_eeprom_stm.c \
        ao_storage.c \
        ao_m25.c \
        ao_usb_stm.c \
        ao_exti_stm.c \
        ao_report.c \
-       ao_i2c_stm.c \
        ao_convert_pa.c \
        ao_convert_volt.c \
        ao_log.c \
@@ -90,7 +79,7 @@ PRODUCT=EasyMega-v3.0
 PRODUCT_DEF=-DEASYMEGA
 IDPRODUCT=0x0028
 
-CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF)
+CFLAGS = $(PRODUCT_DEF) $(STM32F1_CFLAGS)
 
 PROGNAME=easymega-v3.0
 PROG=$(PROGNAME)-$(VERSION).elf
index 70a6e4095dc2231e33e6775bdf2fc994dc0512d3..de666c65170033d0583f47adc3190743aeca878b 100644 (file)
 
 #include <ao.h>
 #include <ao_ms5607.h>
-#include <ao_bmx160.h>
+#include <ao_bmi088.h>
+#include <ao_mmc5983.h>
 #include <ao_adxl375.h>
 #include <ao_log.h>
 #include <ao_exti.h>
 #include <ao_companion.h>
-#include <ao_profile.h>
 #include <ao_eeprom.h>
-#if HAS_SAMPLE_PROFILE
-#include <ao_sample_profile.h>
-#endif
 #include <ao_pyro.h>
-#if HAS_STACK_GUARD
-#include <ao_mpu.h>
-#endif
 
 int
 main(void)
@@ -57,10 +51,10 @@ main(void)
        ao_cmd_init();
 
        ao_ms5607_init();
-       ao_bmx160_init();
+       ao_bmi088_init();
+       ao_mmc5983_init();
        ao_adxl375_init();
 
-       ao_eeprom_init();
        ao_storage_init();
 
        ao_flight_init();
index 79a2a59a35764bfebda67d7453e757bd825abe7c..8f0706ebc1b61ac4f691deab98c6560c809957e1 100644 (file)
 #ifndef _AO_PINS_H_
 #define _AO_PINS_H_
 
+/* 16MHz crystal */
 
-/* 16MHz High speed external crystal */
-#define AO_HSE                 16000000
+#define AO_HSE         1
+#define AO_HSE_BYPASS  0
 
-/* PLLVCO = 96MHz (so that USB will work) */
-#define AO_PLLMUL              6
-#define AO_RCC_CFGR_PLLMUL     (STM_RCC_CFGR_PLLMUL_6)
+#define AO_SYSCLK      72000000
+#define AO_HCLK                72000000
+#define AO_APB1CLK     36000000
+#define AO_APB2CLK     72000000
+#define AO_ADCCLK      12000000
 
-/* SYSCLK = 32MHz (no need to go faster than CPU) */
-#define AO_PLLDIV              3
-#define AO_RCC_CFGR_PLLDIV     (STM_RCC_CFGR_PLLDIV_3)
+/* PLLMUL is 9, PLLXTPRE (pre divider) is 2, so the
+ * overall PLLCLK is 16 * 9/2 = 72MHz (used as SYSCLK)
+ *
+ * HCLK is SYSCLK / 1 (HPRE_DIV) = 72MHz (72MHz max)
+ * USB is PLLCLK / 1.5 (USBPRE)= 48MHz (must be 48MHz)
+ * APB2 is HCLK / 1 (PPRE2_DIV) = 72MHz (72MHz max)
+ * APB1 is HCLK / 2 (PPRE1_DIV) = 36MHz (36MHz max)
+ * ADC is APB2 / 6 (ADCPRE) = 12MHz (14MHz max)
+ */
 
-/* HCLK = 32MHz (CPU clock) */
-#define AO_AHB_PRESCALER       1
+#define AO_RCC_CFGR_USBPRE     STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL     STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE   STM_RCC_CFGR_PLLXTPRE_2
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE1_DIV_2
 #define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
-
-/* Run APB1 at 16MHz (HCLK/2) */
-#define AO_APB1_PRESCALER      2
-#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE2_DIV_2
-
-/* Run APB2 at 16MHz (HCLK/2) */
-#define AO_APB2_PRESCALER      2
-#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_2
-
-#define HAS_SERIAL_1           0
-#define USE_SERIAL_1_STDIN     0
-#define SERIAL_1_PB6_PB7       0
-#define SERIAL_1_PA9_PA10      0
-
-#define HAS_SERIAL_2           0
-#define USE_SERIAL_2_STDIN     0
-#define SERIAL_2_PA2_PA3       0
-#define SERIAL_2_PD5_PD6       0
-
-#define HAS_SERIAL_3           0
-#define USE_SERIAL_3_STDIN     0
-#define SERIAL_3_PB10_PB11     0
-#define SERIAL_3_PC10_PC11     0
-#define SERIAL_3_PD8_PD9       0
-
-#define ao_gps_getchar         ao_serial1_getchar
-#define ao_gps_putchar         ao_serial1_putchar
-#define ao_gps_set_speed       ao_serial1_set_speed
-#define ao_gps_fifo            (ao_stm_usart1.rx_fifo)
+#define AO_RCC_CFGR_ADCPRE     STM_RCC_CFGR_ADCPRE_6
 
 #define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX       (1024 * 1024)
 #define AO_CONFIG_MAX_SIZE                     1024
 #define LOG_ERASE_MARK                         0x55
 #define LOG_MAX_ERASE                          128
-#define AO_LOG_FORMAT                          AO_LOG_FORMAT_EASYMEGA_2
+#define AO_LOG_FORMAT                          AO_LOG_FORMAT_EASYMEGA_3
+#define AO_LOG_NORMALIZED                      1
 
 #define HAS_EEPROM             1
 #define USE_INTERNAL_FLASH     0
-#define USE_EEPROM_CONFIG      1
-#define USE_STORAGE_CONFIG     0
+#define USE_EEPROM_CONFIG      0
+#define USE_STORAGE_CONFIG     1
 #define HAS_USB                        1
 #define HAS_BEEP               1
 #define BEEPER_TIMER           2
 #define HAS_APRS               0
 #define HAS_COMPANION          1
 
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT     (&stm_gpioa)
+#define AO_USB_PULLUP_PIN      8
+
 #define HAS_SPI_1              1
 #define SPI_1_PA5_PA6_PA7      1       /* Barometer */
 #define SPI_1_PB3_PB4_PB5      1       /* Accelerometer */
 #define SPI_1_PE13_PE14_PE15   0
-#define SPI_1_OSPEEDR          STM_OSPEEDR_10MHz
+#define SPI_1_MODE_OUTPUT      STM_GPIO_CR_MODE_OUTPUT_10MHZ
 
 #define HAS_SPI_2              1
 #define SPI_2_PB13_PB14_PB15   1       /* Flash, IMU, Companion */
-#define SPI_2_PD1_PD3_PD4      0
-#define SPI_2_OSPEEDR          STM_OSPEEDR_10MHz
+#define SPI_2_MODE_OUTPUT      STM_GPIO_CR_MODE_OUTPUT_50MHZ
 
-#define HAS_I2C_1              1
-#define I2C_1_PB8_PB9          1
+#define HAS_I2C_1              0
+#define I2C_1_PB8_PB9          0
 
-#define HAS_I2C_2              0
-#define I2C_2_PB10_PB11                0
+#define HAS_I2C_2              1
+#define I2C_2_PB10_PB11                1
 
 #define PACKET_HAS_SLAVE       0
 #define PACKET_HAS_MASTER      0
 
 #define LOW_LEVEL_DEBUG                0
 
-#define LED_PORT_ENABLE                STM_RCC_AHBENR_GPIOAEN
-#define LED_PORT               (&stm_gpioa)
-#define LED_PIN_RED            9
-#define LED_PIN_GREEN          10
-#define AO_LED_RED             (1 << LED_PIN_RED)
-#define AO_LED_GREEN           (1 << LED_PIN_GREEN)
+#define LED_0_PORT             (&stm_gpioa)
+#define LED_0_PIN              9
+#define LED_1_PORT             (&stm_gpioa)
+#define LED_1_PIN              10
+#define AO_LED_RED             (1 << LED_0_PIN)
+#define AO_LED_GREEN           (1 << LED_1_PIN)
 
 #define LEDS_AVAILABLE         (AO_LED_RED | AO_LED_GREEN)
 
 
 /* Pyro C */
 #define AO_PYRO_PORT_2 (&stm_gpiob)
-#define AO_PYRO_PIN_2  11
+#define AO_PYRO_PIN_2  9
 
 /* Pyro D */
 #define AO_PYRO_PORT_3 (&stm_gpiob)
-#define AO_PYRO_PIN_3  10
+#define AO_PYRO_PIN_3  8
 
 /* Drogue */
 #define AO_IGNITER_DROGUE_PORT (&stm_gpioa)
@@ -217,10 +205,6 @@ struct ao_adc {
 
 #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
@@ -299,29 +283,53 @@ struct ao_adc {
 #define AO_M25_SPI_CS_MASK     (1 << AO_M25_SPI_CS_PIN)
 #define AO_M25_SPI_BUS         AO_SPI_2_PB13_PB14_PB15
 
-/*
- * bmx160
- */
+/* BMI088 */
 
-#define HAS_BMX160             1
-#define AO_BMX160_INT_PORT     (&stm_gpioc)
-#define AO_BMX160_INT_PIN      15
-#define AO_BMX160_SPI_BUS      (AO_SPI_2_PB13_PB14_PB15 | AO_SPI_MODE_0)
-#define AO_BMX160_SPI_CS_PORT  (&stm_gpioc)
-#define AO_BMX160_SPI_CS_PIN   13
+#define HAS_BMI088             1
+#define AO_BMI088_SPI_BUS      (AO_SPI_2_PB13_PB14_PB15 | AO_SPI_MODE_0)
+#define AO_BMI088_ACC_CS_PORT  (&stm_gpioc)
+#define AO_BMI088_ACC_CS_PIN   14
+#define AO_BMI088_GYR_CS_PORT  (&stm_gpioc)
+#define AO_BMI088_GYR_CS_PIN   13
 #define HAS_IMU                        1
 
-#define ao_data_along(packet)  ((packet)->bmx160.acc_x)
-#define ao_data_across(packet) (-(packet)->bmx160.acc_y)
-#define ao_data_through(packet)        ((packet)->bmx160.acc_z)
+#define ao_bmi088_along(m)     ((m)->acc.x)
+#define ao_bmi088_across(m)    (-(m)->acc.y)
+#define ao_bmi088_through(m)   ((m)->acc.z)
 
-#define ao_data_roll(packet)   ((packet)->bmx160.gyr_x)
-#define ao_data_pitch(packet)  (-(packet)->bmx160.gyr_y)
-#define ao_data_yaw(packet)    ((packet)->bmx160.gyr_z)
+#define ao_bmi088_roll(m)      ((m)->gyr.x)
+#define ao_bmi088_pitch(m)     (-(m)->gyr.y)
+#define ao_bmi088_yaw(m)       ((m)->gyr.z)
 
-#define ao_data_mag_along(packet)      ((packet)->bmx160.mag_x)
-#define ao_data_mag_across(packet)     (-(packet)->bmx160.mag_y)
-#define ao_data_mag_through(packet)    ((packet)->bmx160.mag_z)
+#define ao_data_along(packet)  ao_bmi088_along(&(packet)->bmi088)
+#define ao_data_across(packet) ao_bmi088_across(&(packet)->bmi088)
+#define ao_data_through(packet)        ao_bmi088_through(&(packet)->bmi088)
+
+#define ao_data_roll(packet)   ao_bmi088_roll(&(packet)->bmi088)
+#define ao_data_pitch(packet)  ao_bmi088_pitch(&(packet)->bmi088)
+#define ao_data_yaw(packet)    ao_bmi088_yaw(&(packet)->bmi088)
+
+/*
+ * MMC5983
+ *
+ *     pin 1 NE corner of chip
+ *
+ *     +along          -Y
+ *     +across         +X
+ *     +through        -Z
+ */
+
+#define HAS_MMC5983            1
+#define MMC5983_I2C            1
+#define AO_MMC5983_I2C_INDEX   STM_I2C_INDEX(2)
+
+#define ao_mmc5983_along(m)            (-(m)->y)
+#define ao_mmc5983_across(m)           ((m)->x)
+#define ao_mmc5983_through(m)          (-(m)->z)
+
+#define ao_data_mag_along(packet)      ao_mmc5983_along(&(packet)->mmc5983)
+#define ao_data_mag_across(packet)     ao_mmc5983_across(&(packet)->mmc5983)
+#define ao_data_mag_through(packet)    ao_mmc5983_through(&(packet)->mmc5983)
 
 /* ADXL375 */
 
@@ -330,12 +338,6 @@ struct ao_adc {
 #define AO_ADXL375_CS_PORT     (&stm_gpioc)
 #define AO_ADXL375_CS_PIN      12
 
-#define AO_ADXL375_INT1_PORT   (&stm_gpiob)
-#define AO_ADXL375_INT1_PIN    8
-
-#define AO_ADXL375_INT2_PORT   (&stm_gpiob)
-#define AO_ADXL375_INT2_PIN    9
-
 #define AO_ADXL375_AXIS                x
 #define AO_ADXL375_INVERT      1
 
index 2b715caf8b385a5166705a938d5740433b806257..e441b0a6d7da481ca8fefda62371dd6f0d75c719 100644 (file)
@@ -5,4 +5,4 @@
 
 TOPDIR=../..
 HARDWARE=easymega-v3.0
-include $(TOPDIR)/stm/Makefile-flash.defs
+include $(TOPDIR)/stm32f1/Makefile-flash.defs
index 324b0eb9d406c5342b73e63dfe214b02ecbf2077..c60f620762b9cec061ae6b6a21484e77628b5e61 100644 (file)
 #ifndef _AO_PINS_H_
 #define _AO_PINS_H_
 
-/* External crystal at 16MHz */
-#define AO_HSE         16000000
+/* 16MHz crystal */
+
+#define AO_HSE         1
+#define AO_HSE_BYPASS  0
+
+#define AO_SYSCLK      72000000
+#define AO_HCLK                72000000
+#define AO_APB1CLK     36000000
+#define AO_APB2CLK     72000000
+#define AO_ADCCLK      12000000
+
+/* PLLMUL is 9, PLLXTPRE (pre divider) is 2, so the
+ * overall PLLCLK is 16 * 9/2 = 72MHz (used as SYSCLK)
+ *
+ * HCLK is SYSCLK / 1 (HPRE_DIV) = 72MHz (72MHz max)
+ * USB is PLLCLK / 1.5 (USBPRE)= 48MHz (must be 48MHz)
+ * APB2 is HCLK / 1 (PPRE2_DIV) = 72MHz (72MHz max)
+ * APB1 is HCLK / 2 (PPRE1_DIV) = 36MHz (36MHz max)
+ * ADC is APB2 / 6 (ADCPRE) = 12MHz (14MHz max)
+ */
+
+#define AO_RCC_CFGR_USBPRE     STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL     STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE   STM_RCC_CFGR_PLLXTPRE_2
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE1_DIV_2
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+#define AO_RCC_CFGR_ADCPRE     STM_RCC_CFGR_ADCPRE_6
 
 #include <ao_flash_stm_pins.h>
 
@@ -32,4 +58,8 @@
 #define AO_BOOT_APPLICATION_VALUE      1
 #define AO_BOOT_APPLICATION_MODE       AO_EXTI_MODE_PULL_UP
 
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT     (&stm_gpioa)
+#define AO_USB_PULLUP_PIN      8
+
 #endif /* _AO_PINS_H_ */
index a47fc9bd7ac2a2f99a38208cf31a25b3a373f9c4..a045e126672d553b60ced4b0f4753803d0d83770 100644 (file)
@@ -58,15 +58,25 @@ CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS)
 PROGNAME=easymini-v2.0
 PROG=$(PROGNAME)-$(VERSION).elf
 HEX=$(PROGNAME)-$(VERSION).ihx
+FLASH_PROG=flash-loader/$(PROGNAME)-altos-flash-$(VERSION).elf
+BOTH_DFU=$(PROGNAME)-combined-$(VERSION).dfu
+
+MAKEBIN=$(TOPDIR)/../ao-tools/ao-makebin/ao-makebin
 
 SRC=$(ALTOS_SRC) ao_easymini.c
 OBJ=$(SRC:.c=.o)
 
-all: $(PROG) $(HEX)
+all: $(PROG) $(HEX) $(BOTH_DFU)
 
 $(PROG): Makefile $(OBJ)
        $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
 
+$(BOTH_DFU): $(PROG) $(FLASH_PROG)
+       $(MAKEBIN) --dfu --output=$@ --base=$(FLASH_ADDR) $(FLASH_PROG) $(PROG)
+
+$(FLASH_PROG): FRC
+       +cd flash-loader && make
+
 $(OBJ): $(INC)
 
 distclean:     clean
@@ -78,3 +88,5 @@ clean:
 install:
 
 uninstall:
+
+FRC:
diff --git a/src/easytimer-v2/.gitignore b/src/easytimer-v2/.gitignore
new file mode 100644 (file)
index 0000000..2e32c6f
--- /dev/null
@@ -0,0 +1,2 @@
+ao_product.h
+easytimer-*.elf
diff --git a/src/easytimer-v2/Makefile b/src/easytimer-v2/Makefile
new file mode 100644 (file)
index 0000000..32b9117
--- /dev/null
@@ -0,0 +1,106 @@
+#
+# AltOS build
+#
+#
+
+include ../samd21/Makefile.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_boot.h \
+       ao_companion.h \
+       ao_data.h \
+       ao_sample.h \
+       ao_pins.h \
+       altitude-pa.h \
+       ao_kalman.h \
+       ao_product.h \
+       ao_task.h \
+       ao_whiten.h \
+       samd21.h \
+       Makefile
+
+SAMD21_ROM=128
+SAMD21_RAM=16
+
+ALTOS_SRC = \
+       ao_boot_chain.c \
+       ao_interrupt.c \
+       ao_product.c \
+       ao_romconfig.c \
+       ao_cmd.c \
+       ao_config.c \
+       ao_task.c \
+       ao_stdio.c \
+       ao_storage.c \
+       ao_panic.c \
+       ao_timer.c \
+       ao_mutex.c \
+       ao_freq.c \
+       ao_dma_samd21.c \
+       ao_spi_samd21.c \
+       ao_data.c \
+       ao_bmi088.c \
+       ao_mmc5983.c \
+       ao_m25.c \
+       ao_adc_samd21.c \
+       ao_beep_samd21.c \
+       ao_usb_samd21.c \
+       ao_exti_samd21.c \
+       ao_convert_volt.c \
+       ao_report.c \
+       ao_sample.c \
+       ao_kalman.c \
+       ao_pyro.c \
+       ao_flight.c \
+       ao_ignite.c \
+       ao_log.c \
+       ao_log_timer.c \
+       $(PROFILE) \
+       $(SAMPLE_PROFILE) \
+       $(STACK_GUARD)
+
+PRODUCT=EasyTimer-v2
+PRODUCT_DEF=-DEASYTIMER_V_2
+IDPRODUCT=0x000d
+
+CFLAGS = $(PRODUCT_DEF) $(SAMD21_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF)
+
+PROGNAME=easytimer-v2
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+FLASH_PROG=flash-loader/$(PROGNAME)-altos-flash-$(VERSION).elf
+BOTH_HEX=$(PROGNAME)-combined-$(VERSION).ihx
+
+ELFTOHEX=$(TOPDIR)/../ao-tools/ao-elftohex/ao-elftohex
+
+SRC=$(ALTOS_SRC) ao_easytimer.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX) $(BOTH_HEX)
+
+$(PROG): Makefile $(OBJ)
+       $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(BOTH_HEX): $(PROG) $(FLASH_PROG)
+       $(ELFTOHEX) --nosym --output=$@ $(FLASH_PROG) $(PROG)
+
+$(OBJ): $(INC)
+
+
+$(FLASH_PROG): FRC
+       +cd flash-loader && make
+
+FRC:
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/easytimer-v2/ao_easytimer.c b/src/easytimer-v2/ao_easytimer.c
new file mode 100644 (file)
index 0000000..45265cb
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_ms5607.h>
+#include <ao_bmi088.h>
+#include <ao_mmc5983.h>
+#include <ao_log.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_dma_samd21.h>
+
+int
+main(void)
+{
+       ao_clock_init();
+
+#if HAS_STACK_GUARD
+       ao_mpu_init();
+#endif
+
+       ao_task_init();
+       ao_timer_init();
+
+       ao_dma_init();
+       ao_spi_init();
+       ao_exti_init();
+
+       ao_adc_init();
+       ao_beep_init();
+       ao_cmd_init();
+
+       ao_storage_init();
+
+       ao_bmi088_init();
+       ao_mmc5983_init();
+
+       ao_flight_init();
+       ao_log_init();
+       ao_report_init();
+
+       ao_usb_init();
+       ao_pyro_init();
+       ao_igniter_init();
+
+       ao_config_init();
+#if AO_PROFILE
+       ao_profile_init();
+#endif
+#if HAS_SAMPLE_PROFILE
+       ao_sample_profile_init();
+#endif
+
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/easytimer-v2/ao_pins.h b/src/easytimer-v2/ao_pins.h
new file mode 100644 (file)
index 0000000..4e93561
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define AO_XOSC                        1
+#define AO_XOSC_FREQ           16000000
+#define AO_XOSC_DIV            256
+#define AO_XOSC_MUL            768
+
+#define AO_AHB_PRESCALER       1
+#define AO_APBA_PRESCALER      1
+
+#define AO_CONFIG_MAX_SIZE     1024
+
+#define HAS_EEPROM             1
+#define USE_INTERNAL_FLASH     0
+#define USE_EEPROM_CONFIG      0
+#define USE_STORAGE_CONFIG     1
+#define HAS_USB                        1
+#define HAS_BEEP               1
+#define HAS_BATTERY_REPORT     1
+#define AO_BEEP_TCC            (&samd21_tcc2)
+#define AO_BEEP_TCC_APBC_MASK  SAMD21_PM_APBCMASK_TCC2
+#define AO_BEEP_PORT           (&samd21_port_a)
+#define AO_BEEP_PIN            16
+#define AO_BEEP_FUNC           SAMD21_PORT_PMUX_FUNC_E
+#define HAS_RADIO              0
+#define HAS_TELEMETRY          0
+#define HAS_APRS               0
+#define HAS_COMPANION          0
+
+#define HAS_SPI_0              1
+#define HAS_SPI_3              1
+#define HAS_SPI_5              1
+
+#define LEDS_AVAILABLE         0
+
+#define HAS_GPS                        0
+#define HAS_FLIGHT             1
+#define HAS_ADC                        1
+#define HAS_ADC_TEMP           1
+#define HAS_LOG                        1
+
+/*
+ * Igniter
+ */
+
+#define HAS_IGNITE             0
+#define HAS_IGNITE_REPORT      1
+#define AO_PYRO_NUM            2
+
+#define AO_SENSE_PYRO(p,n)     ((p)->adc.sense[n])
+#define AO_IGNITER_CLOSED      400
+#define AO_IGNITER_OPEN                60
+
+/* Pyro A */
+#define AO_PYRO_PORT_0         (&samd21_port_a)
+#define AO_PYRO_PIN_0          1
+
+#define AO_ADC_SENSE_A          0
+#define AO_ADC_SENSE_A_PORT     (&samd21_port_a)
+#define AO_ADC_SENSE_A_PIN      2
+
+/* Pyro B */
+#define AO_PYRO_PORT_1         (&samd21_port_b)
+#define AO_PYRO_PIN_1          9
+
+#define AO_ADC_SENSE_B          2
+#define AO_ADC_SENSE_B_PORT     (&samd21_port_b)
+#define AO_ADC_SENSE_B_PIN      8
+
+
+/*
+ * ADC
+ */
+#define AO_DATA_RING           32
+#define AO_ADC_NUM_SENSE       2
+
+struct ao_adc {
+       int16_t                 sense[AO_ADC_NUM_SENSE];
+       int16_t                 v_batt;
+       int16_t                 temp;
+};
+
+#define AO_ADC_DUMP(p) \
+       printf("tick: %5lu A: %5d B: %5d batt: %5d\n", \
+              (p)->tick, \
+               (p)->adc.sense[0], (p)->adc.sense[1], \
+              (p)->adc.v_batt);
+
+#define AO_ADC_V_BATT          10
+#define AO_ADC_V_BATT_PORT     (&samd21_port_b)
+#define AO_ADC_V_BATT_PIN      2
+
+#define AO_ADC_TEMP            SAMD21_ADC_INPUTCTRL_MUXPOS_TEMP
+
+#define AO_NUM_ADC_PIN         (AO_ADC_NUM_SENSE + 1)
+
+#define AO_ADC_PIN0_PORT        AO_ADC_SENSE_A_PORT
+#define AO_ADC_PIN0_PIN         AO_ADC_SENSE_A_PIN
+#define AO_ADC_PIN1_PORT        AO_ADC_SENSE_B_PORT
+#define AO_ADC_PIN1_PIN         AO_ADC_SENSE_B_PIN
+#define AO_ADC_PIN2_PORT       AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN2_PIN                AO_ADC_V_BATT_PIN
+
+#define AO_NUM_ADC             (AO_NUM_ADC_PIN + 1)
+
+#define AO_ADC_SQ0              AO_ADC_SENSE_A
+#define AO_ADC_SQ1              AO_ADC_SENSE_B
+#define AO_ADC_SQ2             AO_ADC_V_BATT
+#define AO_ADC_SQ3             AO_ADC_TEMP
+
+/*
+ * Voltage divider on ADC battery sampler
+ */
+#define AO_BATTERY_DIV_PLUS    100     /* 100k */
+#define AO_BATTERY_DIV_MINUS   27      /* 27k */
+
+/*
+ * Voltage divider on ADC igniter samplers
+ */
+#define AO_IGNITE_DIV_PLUS     100     /* 100k */
+#define AO_IGNITE_DIV_MINUS    27      /* 27k */
+
+/*
+ * ADC reference in decivolts
+ */
+#define AO_ADC_REFERENCE_DV    33
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS          1
+#define AO_M25_SPI_CS_PORT     (&samd21_port_b)
+#define AO_M25_SPI_CS_MASK     (1 << 10)
+#define AO_M25_SPI_BUS         AO_SPI_0_PA04_PA05_PA06
+
+/*
+ *
+ * Here are the required sensor signs:
+ *
+ * +along      nose up
+ * +across     switch screws down
+ * +through    TH down
+ *
+ * With the board aligned to have positive accel for the relevant
+ * axis, looking down from above we have:
+ *
+ * +roll       counter clockwise (nose up)
+ * +pitch      counter clockwise (switch screws down)
+ * +yaw                counter clockwise (TH down)
+ */
+
+
+/*
+ * On EasyTimer v2, bmi088 pin 1 (NE corner of chip) is placed away
+ * from the USB edge of the board. Relative to bmi088 specs, to get
+ * the above values, we need to flip the X and Y axes, assigning
+ * values as follows:
+ *
+ *     +along          -Y      +roll   -Y
+ *     +across         -X      +pitch  -X
+ *     +through        +Z      +yaw    +Z
+ */
+
+#define HAS_BMI088             1
+#define AO_BMI088_SPI_BUS      AO_SPI_5_PB22_PB23_PB03
+#define AO_BMI088_ACC_CS_PORT  (&samd21_port_a)
+#define AO_BMI088_ACC_CS_PIN   10
+#define AO_BMI088_GYR_CS_PORT  (&samd21_port_a)
+#define AO_BMI088_GYR_CS_PIN   11
+#define HAS_IMU                        1
+
+#define ao_bmi088_along(m)     (-(m)->acc.y)
+#define ao_bmi088_across(m)    (-(m)->acc.x)
+#define ao_bmi088_through(m)   ((m)->acc.z)
+
+#define ao_bmi088_roll(m)      (-(m)->gyr.y)
+#define ao_bmi088_pitch(m)     (-(m)->gyr.x)
+#define ao_bmi088_yaw(m)       ((m)->gyr.z)
+
+#define ao_data_along(packet)  ao_bmi088_along(&(packet)->bmi088)
+#define ao_data_across(packet) ao_bmi088_across(&(packet)->bmi088)
+#define ao_data_through(packet)        ao_bmi088_through(&(packet)->bmi088)
+
+#define ao_data_roll(packet)   ao_bmi088_roll(&(packet)->bmi088)
+#define ao_data_pitch(packet)  ao_bmi088_pitch(&(packet)->bmi088)
+#define ao_data_yaw(packet)    ao_bmi088_yaw(&(packet)->bmi088)
+
+/*
+ * MMC5983
+ *
+ *     pin 1 NE corner of chip
+ *
+ *     +along          +Y
+ *     +across         +X
+ *     +through        -Z
+ */
+
+#define HAS_MMC5983                    1
+#define AO_MMC5983_INT_PORT            (&samd21_port_a)
+#define AO_MMC5983_INT_PIN             9
+#define AO_MMC5983_SPI_CLK_PORT                (&samd21_port_a)
+#define AO_MMC5983_SPI_CLK_PIN         23
+#define AO_MMC5983_SPI_MISO_PORT       (&samd21_port_a)
+#define AO_MMC5983_SPI_MISO_PIN                20
+#define AO_MMC5983_SPI_MOSI_PORT       (&samd21_port_a)
+#define AO_MMC5983_SPI_MOSI_PIN                22
+#define AO_MMC5983_SPI_INDEX           (AO_SPI_3_PA22_PA23_PA20 | AO_SPI_MODE_3)
+#define AO_MMC5983_SPI_CS_PORT         (&samd21_port_a)
+#define AO_MMC5983_SPI_CS_PIN          8
+
+#define ao_mmc5983_along(m)            ((m)->y)
+#define ao_mmc5983_across(m)           ((m)->x)
+#define ao_mmc5983_through(m)          (-(m)->z)
+
+#define ao_data_mag_along(packet)      ao_mmc5983_along(&(packet)->mmc5983)
+#define ao_data_mag_across(packet)     ao_mmc5983_across(&(packet)->mmc5983)
+#define ao_data_mag_through(packet)    ao_mmc5983_through(&(packet)->mmc5983)
+
+/*
+ * Monitor
+ */
+
+#define HAS_MONITOR            0
+#define LEGACY_MONITOR         0
+#define HAS_MONITOR_PUT                1
+#define AO_MONITOR_LED         0
+#define HAS_RSSI               0
+
+/*
+ * Logging
+ */
+
+#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX       (192 * 1024)
+#define AO_CONFIG_MAX_SIZE                     1024
+#define LOG_ERASE_MARK                         0x55
+#define LOG_MAX_ERASE                          128
+#define AO_LOG_FORMAT                          AO_LOG_FORMAT_EASYTIMER_2
+#define AO_LOG_NORMALIZED                      1
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/easytimer-v2/flash-loader/Makefile b/src/easytimer-v2/flash-loader/Makefile
new file mode 100644 (file)
index 0000000..bf0094b
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=easytimer-v2
+include $(TOPDIR)/samd21/Makefile-flash.defs
diff --git a/src/easytimer-v2/flash-loader/ao_pins.h b/src/easytimer-v2/flash-loader/ao_pins.h
new file mode 100644 (file)
index 0000000..689fe90
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2024 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#include <ao_flash_samd21_pins.h>
+
+/* Debug connector, pin 5 cs_companion0 PA18 */
+
+#define AO_BOOT_PIN                    1
+#define AO_BOOT_APPLICATION_GPIO       (samd21_port_a)
+#define AO_BOOT_APPLICATION_PIN                18
+#define AO_BOOT_APPLICATION_VALUE      1
+#define AO_BOOT_APPLICATION_MODE       AO_MODE_PULL_UP
+
+/* USB */
+#define HAS_USB                        1
+
+#define AO_XOSC                        1
+#define AO_XOSC_FREQ           16000000
+#define AO_XOSC_DIV            256
+#define AO_XOSC_MUL            768
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/kernel/ao_adc_single.h b/src/kernel/ao_adc_single.h
new file mode 100644 (file)
index 0000000..f9d953c
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_ADC_SINGLE_H_
+#define _AO_ADC_SINGLE_H_
+
+void
+ao_adc_single_get(struct ao_adc *packet);
+
+void
+ao_adc_single_init(void);
+
+#endif /* _AO_ADC_SINGLE_H_ */
index be3e8c7222d1bc48746700a8f1ac32d0d7ac6daf..565ac61e450e6d6a567975761e0b4fcb02bf2b4d 100644 (file)
@@ -883,6 +883,7 @@ ao_config_radio_10mw_set(void)
 
 #endif
 
+#if HAS_BARO
 static void
 ao_config_report_feet_show(void)
 {
@@ -899,7 +900,7 @@ ao_config_report_feet_set(void)
        ao_config.report_feet = !!r;
        _ao_config_edit_finish();
 }
-
+#endif
 
 #if HAS_BEEP
 static void
@@ -1152,8 +1153,10 @@ const struct ao_config_var ao_config_vars[] = {
        { "p <0 no limit, 1 limit>\0Limit radio power to 10mW",
          ao_config_radio_10mw_set,     ao_config_radio_10mw_show },
 #endif
+#if HAS_BARO
        { "u <0 meters, 1 feet>\0Units to report height after landing",
          ao_config_report_feet_set,    ao_config_report_feet_show },
+#endif
        { "s\0Show",
          ao_config_show,               0 },
 #if HAS_CONFIG_SAVE
index 8545c7d4ddfabfa10763d4b79095ef6bf410ad54..a6ae6fa84b3efe1158901da889c9daea8e07e22d 100644 (file)
@@ -503,6 +503,18 @@ static inline float ao_convert_accel(int16_t sensor)
 
 #endif
 
+#if !HAS_ACCEL && HAS_BMI088
+
+#define HAS_ACCEL      1
+
+typedef int16_t accel_t;
+
+#define ao_data_accel_raw(packet)              -ao_data_along(packet)
+#define ao_data_accel_invert(a)                        (-(a))
+#define ao_data_accel_to_sample(accel)         ao_bmi_accel_to_sample(accel)
+
+#endif
+
 #if !HAS_GYRO && HAS_BMI088
 
 #define HAS_GYRO       1
index 8c0e7db4f7e51c4f6e7f92d092eacb3c6faa6713..209581b85efd28d2ca193c6e7c6e65686c6dbd25 100644 (file)
@@ -74,7 +74,7 @@ ao_fec_prepare(const uint8_t *in, uint8_t len, uint8_t *extra)
        extra[i++] = (uint8_t) crc;
 
        /* Append FEC -- 1 byte if odd, two bytes if even */
-       num_fec = 2 - (i & 1);
+       num_fec = 2 - (len & 1);
        while (num_fec--)
                extra[i++] = AO_FEC_TRELLIS_TERMINATOR;
        return i;
index 9df34b4e566cf383daf1c573e4458c9da1778afe..b8aa6a8c589541759f50a34885cff2edf53897e5 100644 (file)
@@ -62,6 +62,8 @@ extern enum ao_flight_state ao_log_state;
 #define AO_LOG_FORMAT_EASYMOTOR                20      /* 16 byte typed easymotor records with pressure sensor and adxl375 */
 #define AO_LOG_FORMAT_TELEMEGA_5       21      /* 32 byte typed telemega records with 32 bit gyro cal, mpu6000 and mmc5983 */
 #define AO_LOG_FORMAT_TELEMEGA_6       22      /* 32 byte typed telemega records with 32 bit gyro cal, bmi088 and mmc5983 */
+#define AO_LOG_FORMAT_EASYTIMER_2      23      /* 32 byte typed easytimer records with 32 bit gyro cal, bmi088 and mmc5983 */
+#define AO_LOG_FORMAT_EASYMEGA_3       24      /* 32 byte typed telemega records with 32 bit gyro cal, bmi088 and mmc5983 */
 #define AO_LOG_FORMAT_NONE             127     /* No log at all */
 
 /* Return the flight number from the given log slot, 0 if none, -slot on failure */
@@ -550,7 +552,48 @@ struct ao_log_motor {
        } u;
 };
 
-#if AO_LOG_FORMAT == AO_LOG_FOMAT_TELEMEGA_OLD || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_3 || AO_LOG_FORMAT == AO_LOG_FORMAT_EASYMEGA_2 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_4 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_5 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_6
+struct ao_log_timer {
+       char                    type;                   /* 0 */
+       uint8_t                 csum;                   /* 1 */
+       uint16_t                tick;                   /* 2 */
+       union {                                         /* 4 */
+               /* AO_LOG_FLIGHT */
+               struct {
+                       uint16_t        flight;                 /* 4 */
+                       int16_t         ground_accel;           /* 6 */
+                       int16_t         ground_accel_along;     /* 8 */
+                       int16_t         ground_accel_across;    /* 10 */
+                       int16_t         ground_accel_through;   /* 12 */
+                       int16_t         pad_14;                 /* 14 */
+                       int32_t         ground_roll;            /* 16 */
+                       int32_t         ground_pitch;           /* 20 */
+                       int32_t         ground_yaw;             /* 24 */
+               } flight;                                       /* 32 */
+               /* AO_LOG_STATE */
+               struct {
+                       uint16_t        state;                  /* 4 */
+                       uint16_t        reason;                 /* 6 */
+               } state;
+               /* AO_LOG_SENSOR */
+               struct {
+                       int16_t         accel_along;    /* 4 */
+                       int16_t         accel_across;   /* 6 */
+                       int16_t         accel_through;  /* 8 */
+                       int16_t         gyro_roll;      /* 10 */
+                       int16_t         gyro_pitch;     /* 12 */
+                       int16_t         gyro_yaw;       /* 14 */
+                       int16_t         mag_along;      /* 16 */
+                       int16_t         mag_across;     /* 18 */
+                       int16_t         mag_through;    /* 20 */
+                       int16_t         v_batt;         /* 22 */
+                       int16_t         v_pbatt;        /* 24 */
+                       int16_t         sense[2];       /* 26 */
+                       uint16_t        pyro;           /* 30 */
+               } sensor;       /* 32 */
+       } u;
+};
+
+#if AO_LOG_FORMAT == AO_LOG_FOMAT_TELEMEGA_OLD || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_3 || AO_LOG_FORMAT == AO_LOG_FORMAT_EASYMEGA_2 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_4 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_5 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_6 || AO_LOG_FORMAT == AO_LOG_FORMAT_EASYMEGA_3
 typedef struct ao_log_mega ao_log_type;
 #endif
 
@@ -594,6 +637,10 @@ typedef struct ao_log_record ao_log_type;
 #define AO_LOG_UNCOMMON        1
 #endif
 
+#if AO_LOG_FORMAT == AO_LOG_FORMAT_EASYTIMER_2
+typedef struct ao_log_timer ao_log_type;
+#endif
+
 #ifndef AO_LOG_UNCOMMON
 extern ao_log_type ao_log_data;
 
diff --git a/src/kernel/ao_log_timer.c b/src/kernel/ao_log_timer.c
new file mode 100644 (file)
index 0000000..42b9b85
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright © 2024 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_log.h>
+#include <ao_data.h>
+#include <ao_flight.h>
+
+#if HAS_FLIGHT
+static uint8_t ao_log_data_pos;
+
+/* a hack to make sure that ao_log_timers fill the eeprom block in even units */
+typedef uint8_t check_log_size[1-(256 % sizeof(struct ao_log_timer))] ;
+
+#ifndef AO_SENSOR_INTERVAL_ASCENT
+#define AO_SENSOR_INTERVAL_ASCENT      1
+#define AO_SENSOR_INTERVAL_DESCENT     10
+#define AO_OTHER_INTERVAL              32
+#endif
+
+void
+ao_log(void)
+{
+       AO_TICK_TYPE            next_sensor;
+       uint8_t                 i;
+
+       ao_storage_setup();
+
+       ao_log_scan();
+
+       while (!ao_log_running)
+               ao_sleep(&ao_log_running);
+
+#if HAS_FLIGHT
+       ao_log_data.type = AO_LOG_FLIGHT;
+       ao_log_data.tick = (uint16_t) ao_sample_tick;
+#if HAS_ACCEL
+       ao_log_data.u.flight.ground_accel = ao_ground_accel;
+#endif
+#if HAS_GYRO
+       ao_log_data.u.flight.ground_accel_along = ao_ground_accel_along;
+       ao_log_data.u.flight.ground_accel_across = ao_ground_accel_across;
+       ao_log_data.u.flight.ground_accel_through = ao_ground_accel_through;
+       ao_log_data.u.flight.ground_roll = ao_ground_roll;
+       ao_log_data.u.flight.ground_pitch = ao_ground_pitch;
+       ao_log_data.u.flight.ground_yaw = ao_ground_yaw;
+#endif
+       ao_log_data.u.flight.flight = ao_flight_number;
+       ao_log_write(&ao_log_data);
+#endif
+
+       /* Write the whole contents of the ring to the log
+        * when starting up.
+        */
+       ao_log_data_pos = ao_data_ring_next(ao_data_head);
+       next_sensor = ao_data_ring[ao_log_data_pos].tick;
+       ao_log_state = ao_flight_startup;
+       for (;;) {
+               /* Write samples to EEPROM */
+               while (ao_log_data_pos != ao_data_head) {
+                       AO_TICK_TYPE tick = ao_data_ring[ao_log_data_pos].tick;
+                       ao_log_data.tick = (uint16_t) tick;
+                       volatile struct ao_data *d = &ao_data_ring[ao_log_data_pos];
+                       if ((AO_TICK_SIGNED) (tick - next_sensor) >= 0) {
+                               ao_log_data.type = AO_LOG_SENSOR;
+
+#if HAS_IMU
+                               ao_log_data.u.sensor.accel_along = ao_data_along(d);
+                               ao_log_data.u.sensor.accel_across = ao_data_across(d);
+                               ao_log_data.u.sensor.accel_through = ao_data_through(d);
+                               ao_log_data.u.sensor.gyro_roll = ao_data_roll(d);
+                               ao_log_data.u.sensor.gyro_pitch = ao_data_pitch(d);
+                               ao_log_data.u.sensor.gyro_yaw = ao_data_yaw(d);
+#endif
+#if HAS_MAG
+                               ao_log_data.u.sensor.mag_along = ao_data_mag_along(d);
+                               ao_log_data.u.sensor.mag_across = ao_data_mag_across(d);
+                               ao_log_data.u.sensor.mag_through = ao_data_mag_through(d);
+#endif
+                               ao_log_data.u.sensor.v_batt = d->adc.v_batt;
+                               for (i = 0; i < AO_ADC_NUM_SENSE; i++)
+                                       ao_log_data.u.sensor.sense[i] = d->adc.sense[i];
+                               ao_log_data.u.sensor.pyro = ao_pyro_fired;
+
+                               ao_log_write(&ao_log_data);
+                               if (ao_log_state <= ao_flight_coast)
+                                       next_sensor = tick + AO_SENSOR_INTERVAL_ASCENT;
+                               else
+                                       next_sensor = tick + AO_SENSOR_INTERVAL_DESCENT;
+                       }
+                       ao_log_data_pos = ao_data_ring_next(ao_log_data_pos);
+               }
+#if HAS_FLIGHT
+               /* Write state change to EEPROM */
+               if (ao_flight_state != ao_log_state) {
+                       ao_log_state = ao_flight_state;
+                       ao_log_data.type = AO_LOG_STATE;
+                       ao_log_data.tick = (uint16_t) ao_time();
+                       ao_log_data.u.state.state = ao_log_state;
+                       ao_log_data.u.state.reason = 0;
+                       ao_log_write(&ao_log_data);
+
+                       if (ao_log_state == ao_flight_landed)
+                               ao_log_stop();
+               }
+#endif
+
+               ao_log_flush();
+
+               /* Wait for a while */
+               ao_delay(AO_MS_TO_TICKS(100));
+
+               /* Stop logging when told to */
+               while (!ao_log_running)
+                       ao_sleep(&ao_log_running);
+       }
+}
+#endif /* HAS_FLIGHT */
+
index c40c50df42befd1128512a919f7ab9323055246f..4c69b82a349c340e4831b42375f5d5f58417425d 100644 (file)
@@ -684,9 +684,11 @@ ao_pyro_update_version(void)
 
                        for (v = 0; v < NUM_PYRO_VALUES; v++)
                        {
-                               value = ao_pyro_get_1_24(&pyro_1_24[p], ao_pyro_values[v].flag);
-                               ao_pyro_put(&tmp, ao_pyro_values[v].offset,
-                                           ao_pyro_size(ao_pyro_values[v].flag), value);
+                               if (ao_pyro_values[v].offset != NO_VALUE) {
+                                       value = ao_pyro_get_1_24(&pyro_1_24[p], ao_pyro_values[v].flag);
+                                       ao_pyro_put(&tmp, ao_pyro_values[v].offset,
+                                                   ao_pyro_size(ao_pyro_values[v].flag), value);
+                               }
                        }
                        memcpy(&pyro_1_25[p], &tmp, sizeof(tmp));
                }
index 29d3668cedb1fdff4824dfa5ba2494610604ab81..1fbf13660a5969739cf496d602acf699e4abb371 100644 (file)
 /* ADC clock is divided by this value + 1, which ensures that
  * the ADC clock will be strictly less than 4.5MHz as required
  */
-#define AO_ADC_CLKDIV  (AO_LPC_SYSCLK / 450000)
+#ifndef AO_LPC_ADC_CLOCK
+#define AO_LPC_ADC_CLOCK       4500000
+#endif
+#define AO_ADC_CLKDIV  (AO_LPC_SYSCLK / AO_LPC_ADC_CLOCK)
 
 static uint8_t         ao_adc_ready;
 static uint8_t         ao_adc_sequence;
index 59f3a7eee4983a94fee99c4bf19d716725010811..05bb97d9cb3a8cbf9c7fe314f512a095fede33f5 100644 (file)
@@ -116,8 +116,9 @@ ao_serial_init(void);
 #define _AO_SPI_SPEED_62500Hz          768
 
 static inline uint32_t
-ao_spi_speed(uint32_t hz)
+ao_spi_speed(int index, uint32_t hz)
 {
+       (void) index;
        if (hz >= 4000000) return _AO_SPI_SPEED_4MHz;
        if (hz >= 2000000) return _AO_SPI_SPEED_2MHz;
        if (hz >= 1000000) return _AO_SPI_SPEED_1MHz;
index 32670b45ca1897bc1f6a3016a4fd9c1cf4902930..754ca3b51c3c84ecc4c8a6e2079ea64c8c8f54ba 100644 (file)
@@ -282,10 +282,11 @@ ao_spi_put_bit(struct samd21_port *port, uint8_t bit, uint16_t spi_index)
 }
 
 static inline uint8_t
-ao_spi_speed(uint32_t hz)
+ao_spi_speed(int index, uint32_t hz)
 {
        int32_t baud = (int32_t) (AO_SYSCLK / (2 * hz)) - 1;
 
+       (void) index;
        if (baud < 1)
                baud = 1;
        if (baud > 255)
index bed4f54adcd59e8a14912b0e510bf48e9536d14f..6b88e8564832b8a4d2ba2b5fca014009ac9148d7 100644 (file)
 #define AO_USB_DEVICE_ID_SERIAL 0
 #endif
 
+#ifndef USE_USB_STDIO
+#define USE_USB_STDIO  1
+#endif
+
 #if USE_USB_FIFO
 static struct ao_fifo  ao_usb_rx_fifo;
 #endif
index 719ea01dd454230005de6af442be839e8d5b9a24..a829aa132f408aab31a5ea6ba0fe3b2af39efe34 100644 (file)
@@ -19,7 +19,7 @@
 #define SNEK_CS_PORT   (&samd21_port_a)
 #define SNEK_CS_PIN    (11)
 #define SNEK_SPI_INDEX AO_SPI_0_PA08_PA09_PA10
-#define SNEK_SPI_SPEED ao_spi_speed(1000000)
+#define SNEK_SPI_SPEED ao_spi_speed(SNEK_SPI_INDEX, 1000000)
 
 static const uint8_t spi_test[] = {
        0x55,
diff --git a/src/stm/ao_adc_single.h b/src/stm/ao_adc_single.h
deleted file mode 100644 (file)
index f9d953c..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright © 2018 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- */
-
-#ifndef _AO_ADC_SINGLE_H_
-#define _AO_ADC_SINGLE_H_
-
-void
-ao_adc_single_get(struct ao_adc *packet);
-
-void
-ao_adc_single_init(void);
-
-#endif /* _AO_ADC_SINGLE_H_ */
index 9922513be007bf005c19c9d9d1cd5021e3266300..0f2f36a6e40aa0a6a64b147c02508ade2d2e380c 100644 (file)
@@ -34,8 +34,9 @@
 #define _AO_SPI_SPEED_62500Hz  STM_SPI_CR1_BR_PCLK_256
 
 static inline uint32_t
-ao_spi_speed(uint32_t hz)
+ao_spi_speed(int index, uint32_t hz)
 {
+       (void) index;
        if (hz >= 4000000) return _AO_SPI_SPEED_4MHz;
        if (hz >= 2000000) return _AO_SPI_SPEED_2MHz;
        if (hz >= 1000000) return _AO_SPI_SPEED_1MHz;
diff --git a/src/stm32f1/Makefile-flash.defs b/src/stm32f1/Makefile-flash.defs
new file mode 100644 (file)
index 0000000..461d929
--- /dev/null
@@ -0,0 +1,58 @@
+include $(TOPDIR)/stm32f1/Makefile-stm32f1.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_flash_pins.h \
+       ao_flash_stm_pins.h \
+       ao_flash_task.h \
+       ao_pins.h \
+       ao_product.h \
+       Makefile
+
+#
+# Common AltOS sources
+#
+SRC = \
+       ao_interrupt.c \
+       ao_romconfig.c \
+       ao_boot_chain.c \
+       ao_boot_pin.c \
+       ao_product.c \
+       ao_notask.c \
+       ao_clock.c \
+       ao_usb_stm.c \
+       ao_flash_stm.c \
+       ao_flash_task.c \
+       ao_flash_loader_stm.c
+
+OBJ=$(SRC:.c=.o)
+
+PRODUCT=AltosFlash
+PRODUCT_DEF=-DALTOS_FLASH
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(STM32F1_CFLAGS)
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f1 -Taltos-loader.ld -n -Wl,--gc-sections
+
+PROGNAME=$(HARDWARE)-altos-flash
+PROG=$(PROGNAME)-$(VERSION).elf
+
+$(PROG): Makefile $(OBJ) altos-loader.ld
+       $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+all: $(PROG)
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.map
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/stm32f1/Makefile-raw.defs b/src/stm32f1/Makefile-raw.defs
new file mode 100644 (file)
index 0000000..a3894ca
--- /dev/null
@@ -0,0 +1,13 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/stm32f1/Makefile-stm32f1.defs
+
+LOADER=flash-loader/$(PROGNAME)-altos-flash-$(VERSION).elf
+MAKEBIN=$(TOPDIR)/../ao-tools/ao-makebin/ao-makebin
+FLASH_ADDR=0x08000000
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f1 -Taltos-raw.ld -n
+
+.DEFAULT_GOAL=all
diff --git a/src/stm32f1/Makefile-stm32f1.defs b/src/stm32f1/Makefile-stm32f1.defs
new file mode 100644 (file)
index 0000000..b84f739
--- /dev/null
@@ -0,0 +1,12 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/Makefile.defs
+
+vpath % $(TOPDIR)/stm32f1:$(AO_VPATH)
+
+CC=$(ARM_CC)
+
+STM32F1_CFLAGS=-mlittle-endian -mcpu=cortex-m3 -mthumb \
+       -I$(TOPDIR)/stm32f1 $(AO_CFLAGS) $(PICOLIBC_CFLAGS)
diff --git a/src/stm32f1/Makefile.defs b/src/stm32f1/Makefile.defs
new file mode 100644 (file)
index 0000000..5ff7508
--- /dev/null
@@ -0,0 +1,11 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/stm32f1/Makefile-stm32f1.defs
+
+STM32F1_LINKER_SCRIPT?=altos.ld
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f1 -T$(STM32F1_LINKER_SCRIPT) -n -Wl,--gc-sections
+
+.DEFAULT_GOAL=all
diff --git a/src/stm32f1/altos-128.ld b/src/stm32f1/altos-128.ld
new file mode 100644 (file)
index 0000000..106e74e
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2024 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+__flash       = 0x08001000;
+__eeprom_base = 0x0801f800;
+__flash_size = 122K;
+__ram = 0x20000000;
+__ram_size = 20k;
+__stack_size = 512;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
diff --git a/src/stm32f1/altos-loader.ld b/src/stm32f1/altos-loader.ld
new file mode 100644 (file)
index 0000000..798106e
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+__flash = 0x08000000;
+__flash_size = 4k;
+__ram = 0x20000000;
+__ram_size = 20k;
+__stack_size = 512;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
diff --git a/src/stm32f1/altos-ram.ld b/src/stm32f1/altos-ram.ld
new file mode 100644 (file)
index 0000000..278d940
--- /dev/null
@@ -0,0 +1,8 @@
+__flash = 0x20000000;
+__flash_size = 12k;
+__ram   = 0x20003000;
+__ram_size = 8k;
+__stack_size = 512;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
diff --git a/src/stm32f1/altos-raw.ld b/src/stm32f1/altos-raw.ld
new file mode 100644 (file)
index 0000000..1a7ccdd
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+__flash = 0x08000000;
+__flash_size = 64k;
+__ram = 0x20000000;
+__ram_size = 20k;
+__stack_size = 512;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
diff --git a/src/stm32f1/altos.ld b/src/stm32f1/altos.ld
new file mode 100644 (file)
index 0000000..8549311
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+__flash       = 0x08001000;
+__eeprom_base = 0x0800e800;
+__flash_size = 58K;
+__ram = 0x20000000;
+__ram_size = 20k;
+__stack_size = 512;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
diff --git a/src/stm32f1/ao_adc_single_stm.c b/src/stm32f1/ao_adc_single_stm.c
new file mode 100644 (file)
index 0000000..feaba42
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * Copyright © 2024 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_data.h>
+#include <ao_adc_single.h>
+
+static uint8_t                 ao_adc_ready;
+
+#define AO_ADC_CR2_VAL(start)  ((HAS_ADC_TEMP << STM_ADC_CR2_TSVREFE) |\
+                                ((start) << STM_ADC_CR2_SWSTART) |     \
+                                (0 << STM_ADC_CR2_JWSTART) |           \
+                                (0 << STM_ADC_CR2_EXTTRIG) |           \
+                                (STM_ADC_CR2_EXTSEL_SWSTART << STM_ADC_CR2_EXTSEL) | \
+                                (0 << STM_ADC_CR2_JEXTTRIG) |          \
+                                (0 << STM_ADC_CR2_JEXTSEL) |           \
+                                (0 << STM_ADC_CR2_ALIGN) |             \
+                                (1 << STM_ADC_CR2_DMA) |               \
+                                (0 << STM_ADC_CR2_CONT) |              \
+                                (1 << STM_ADC_CR2_ADON))
+
+/*
+ * Callback from DMA ISR
+ *
+ * Shut down DMA engine, signal anyone waiting
+ */
+static void ao_adc_done(int index)
+{
+       (void) index;
+       ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+       ao_adc_ready = 1;
+       /* Turn the ADC back off */
+       stm_adc1.cr2 = 0;
+       ao_wakeup((void *) &ao_adc_ready);
+}
+
+/*
+ * Start the ADC sequence using the DMA engine
+ */
+static void
+ao_adc_poll(struct ao_adc *packet)
+{
+       ao_adc_ready = 0;
+       stm_adc1.sr = 0;
+       ao_dma_set_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1),
+                           &stm_adc1.dr,
+                           (void *) packet,
+                           AO_NUM_ADC,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) |
+                           (1 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+       ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1), ao_adc_done);
+       ao_dma_start(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+
+       stm_adc1.cr2 = AO_ADC_CR2_VAL(0);
+       ao_delay(AO_MS_TO_TICKS(10));
+       stm_adc1.cr2 = AO_ADC_CR2_VAL(0);
+       ao_delay(AO_MS_TO_TICKS(10));
+       stm_adc1.cr2 = AO_ADC_CR2_VAL(1);
+}
+
+/*
+ * Fetch a copy of the most recent ADC data
+ */
+void
+ao_adc_single_get(struct ao_adc *packet)
+{
+       ao_adc_poll(packet);
+       ao_arch_block_interrupts();
+       while (!ao_adc_ready)
+               ao_sleep(&ao_adc_ready);
+       ao_arch_release_interrupts();
+}
+
+static void
+ao_adc_dump(void)
+{
+       struct ao_adc   packet;
+       ao_adc_single_get(&packet);
+       AO_ADC_DUMP(&packet);
+}
+
+const struct ao_cmds ao_adc_cmds[] = {
+       { ao_adc_dump,  "a\0Display current ADC values" },
+       { 0, NULL },
+};
+
+static inline void
+adc_pin_set(struct stm_gpio *gpio, int pin)
+{
+       ao_enable_port(gpio);
+       stm_gpio_conf(gpio, pin,
+                     STM_GPIO_CR_MODE_INPUT,
+                     STM_GPIO_CR_CNF_INPUT_ANALOG);
+}
+
+void
+ao_adc_single_init(void)
+{
+#ifdef AO_ADC_PIN0_PORT
+       adc_pin_set(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN);
+#endif
+#ifdef AO_ADC_PIN1_PORT
+       adc_pin_set(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN);
+#endif
+#ifdef AO_ADC_PIN2_PORT
+       adc_pin_set(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN);
+#endif
+#ifdef AO_ADC_PIN3_PORT
+       adc_pin_set(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN);
+#endif
+#ifdef AO_ADC_PIN4_PORT
+       adc_pin_set(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN);
+#endif
+#ifdef AO_ADC_PIN5_PORT
+       adc_pin_set(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN);
+#endif
+#ifdef AO_ADC_PIN6_PORT
+       adc_pin_set(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN);
+#endif
+#ifdef AO_ADC_PIN7_PORT
+       adc_pin_set(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN);
+#endif
+#ifdef AO_ADC_PIN8_PORT
+       adc_pin_set(AO_ADC_PIN8_PORT, AO_ADC_PIN8_PIN);
+#endif
+#ifdef AO_ADC_PIN9_PORT
+       adc_pin_set(AO_ADC_PIN9_PORT, AO_ADC_PIN9_PIN);
+#endif
+#ifdef AO_ADC_PIN10_PORT
+       adc_pin_set(AO_ADC_PIN10_PORT, AO_ADC_PIN10_PIN);
+#endif
+#ifdef AO_ADC_PIN11_PORT
+       adc_pin_set(AO_ADC_PIN11_PORT, AO_ADC_PIN11_PIN);
+#endif
+#ifdef AO_ADC_PIN12_PORT
+       adc_pin_set(AO_ADC_PIN12_PORT, AO_ADC_PIN12_PIN);
+#endif
+#ifdef AO_ADC_PIN13_PORT
+       adc_pin_set(AO_ADC_PIN13_PORT, AO_ADC_PIN13_PIN);
+#endif
+#ifdef AO_ADC_PIN14_PORT
+       adc_pin_set(AO_ADC_PIN14_PORT, AO_ADC_PIN14_PIN);
+#endif
+#ifdef AO_ADC_PIN15_PORT
+       adc_pin_set(AO_ADC_PIN15_PORT, AO_ADC_PIN15_PIN);
+#endif
+#ifdef AO_ADC_PIN16_PORT
+       adc_pin_set(AO_ADC_PIN16_PORT, AO_ADC_PIN16_PIN);
+#endif
+#ifdef AO_ADC_PIN17_PORT
+       adc_pin_set(AO_ADC_PIN17_PORT, AO_ADC_PIN17_PIN);
+#endif
+#ifdef AO_ADC_PIN18_PORT
+       adc_pin_set(AO_ADC_PIN18_PORT, AO_ADC_PIN18_PIN);
+#endif
+#ifdef AO_ADC_PIN19_PORT
+       adc_pin_set(AO_ADC_PIN19_PORT, AO_ADC_PIN19_PIN);
+#endif
+#ifdef AO_ADC_PIN20_PORT
+       adc_pin_set(AO_ADC_PIN20_PORT, AO_ADC_PIN20_PIN);
+#endif
+#ifdef AO_ADC_PIN21_PORT
+       adc_pin_set(AO_ADC_PIN21_PORT, AO_ADC_PIN21_PIN);
+#endif
+#ifdef AO_ADC_PIN22_PORT
+       adc_pin_set(AO_ADC_PIN22_PORT, AO_ADC_PIN22_PIN);
+#endif
+#ifdef AO_ADC_PIN23_PORT
+       adc_pin_set(AO_ADC_PIN23_PORT, AO_ADC_PIN23_PIN);
+#endif
+#ifdef AO_ADC_PIN24_PORT
+       #error "Too many ADC ports"
+#endif
+
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_ADC1EN);
+
+       /* Turn off ADC during configuration */
+       stm_adc1.cr2 = 0;
+
+       stm_adc1.cr1 = ((0 << STM_ADC_CR1_AWDEN ) |
+                      (0 << STM_ADC_CR1_JAWDEN ) |
+                      (STM_ADC_CR1_DUALMOD_INDEPENDENT << STM_ADC_CR1_DUALMOD ) |
+                      (0 << STM_ADC_CR1_DISCNUM ) |
+                      (0 << STM_ADC_CR1_JDISCEN ) |
+                      (0 << STM_ADC_CR1_DISCEN ) |
+                      (0 << STM_ADC_CR1_JAUTO ) |
+                      (0 << STM_ADC_CR1_AWDSGL ) |
+                      (1 << STM_ADC_CR1_SCAN ) |
+                      (0 << STM_ADC_CR1_JEOCIE ) |
+                      (0 << STM_ADC_CR1_AWDIE ) |
+                      (0 << STM_ADC_CR1_EOCIE ) |
+                      (0 << STM_ADC_CR1_AWDCH ));
+
+       /* 384 cycle sample time for everyone */
+       stm_adc1.smpr1 = 0x00ffffff;
+       stm_adc1.smpr2 = 0x3fffffff;
+
+       stm_adc1.sqr1 = ((AO_NUM_ADC - 1) << 20);
+#if AO_NUM_ADC > 0
+       stm_adc1.sqr3 |= (AO_ADC_SQ1 << 0);
+#endif
+#if AO_NUM_ADC > 1
+       stm_adc1.sqr3 |= (AO_ADC_SQ2 << 5);
+#endif
+#if AO_NUM_ADC > 2
+       stm_adc1.sqr3 |= (AO_ADC_SQ3 << 10);
+#endif
+#if AO_NUM_ADC > 3
+       stm_adc1.sqr3 |= (AO_ADC_SQ4 << 15);
+#endif
+#if AO_NUM_ADC > 4
+       stm_adc1.sqr3 |= (AO_ADC_SQ5 << 20);
+#endif
+#if AO_NUM_ADC > 5
+       stm_adc1.sqr3 |= (AO_ADC_SQ6 << 25);
+#endif
+#if AO_NUM_ADC > 6
+       stm_adc1.sqr2 |= (AO_ADC_SQ7 << 0);
+#endif
+#if AO_NUM_ADC > 7
+       stm_adc1.sqr2 |= (AO_ADC_SQ8 << 5);
+#endif
+#if AO_NUM_ADC > 8
+       stm_adc1.sqr2 |= (AO_ADC_SQ9 << 10);
+#endif
+#if AO_NUM_ADC > 9
+       stm_adc1.sqr2 |= (AO_ADC_SQ10 << 15);
+#endif
+#if AO_NUM_ADC > 10
+       stm_adc1.sqr2 |= (AO_ADC_SQ11 << 20);
+#endif
+#if AO_NUM_ADC > 11
+       stm_adc1.sqr2 |= (AO_ADC_SQ12 << 25);
+#endif
+#if AO_NUM_ADC > 12
+       stm_adc1.sqr1 |= (AO_ADC_SQ13 << 0);
+#endif
+#if AO_NUM_ADC > 13
+       stm_adc1.sqr1 |= (AO_ADC_SQ14 << 5);
+#endif
+#if AO_NUM_ADC > 14
+       stm_adc1.sqr1 |= (AO_ADC_SQ15 << 10);
+#endif
+#if AO_NUM_ADC > 15
+       stm_adc1.sqr1 |= (AO_ADC_SQ16 << 15);
+#endif
+#if AO_NUM_ADC > 15
+#error "too many ADC channels"
+#endif
+
+       /* Clear any stale status bits */
+       stm_adc1.sr = 0;
+
+       ao_dma_alloc(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+
+       ao_cmd_register(&ao_adc_cmds[0]);
+}
diff --git a/src/stm32f1/ao_adc_stm.c b/src/stm32f1/ao_adc_stm.c
new file mode 100644 (file)
index 0000000..3602144
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_data.h>
+
+static uint8_t                 ao_adc_ready;
+
+#define AO_ADC_CR2_VAL(start)  ((HAS_ADC_TEMP << STM_ADC_CR2_TSVREFE) |\
+                                ((start) << STM_ADC_CR2_SWSTART) |     \
+                                (0 << STM_ADC_CR2_JWSTART) |           \
+                                (0 << STM_ADC_CR2_EXTTRIG) |           \
+                                (STM_ADC_CR2_EXTSEL_SWSTART << STM_ADC_CR2_EXTSEL) | \
+                                (0 << STM_ADC_CR2_JEXTTRIG) |          \
+                                (0 << STM_ADC_CR2_JEXTSEL) |           \
+                                (0 << STM_ADC_CR2_ALIGN) |             \
+                                (1 << STM_ADC_CR2_DMA) |               \
+                                (0 << STM_ADC_CR2_CONT) |              \
+                                (1 << STM_ADC_CR2_ADON))
+
+/*
+ * Callback from DMA ISR
+ *
+ * Mark time in ring, shut down DMA engine
+ */
+static void ao_adc_done(int index)
+{
+       (void) index;
+       AO_DATA_PRESENT(AO_DATA_ADC);
+       ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+       ao_data_fill(ao_data_head);
+       ao_adc_ready = 1;
+}
+
+/*
+ * Start the ADC sequence using the DMA engine
+ */
+void
+ao_adc_poll(void)
+{
+       if (!ao_adc_ready)
+               return;
+       ao_adc_ready = 0;
+       stm_adc1.sr = 0;
+       ao_dma_set_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1),
+                           &stm_adc1.dr,
+                           (void *) (&ao_data_ring[ao_data_head].adc),
+                           AO_NUM_ADC,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) |
+                           (1 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+       ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1), ao_adc_done);
+       ao_dma_start(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+
+       stm_adc1.cr2 = AO_ADC_CR2_VAL(0);
+       stm_adc1.cr2 = AO_ADC_CR2_VAL(0);
+       stm_adc1.cr2 = AO_ADC_CR2_VAL(1);
+}
+
+#ifdef AO_ADC_SQ1_NAME
+static const char *ao_adc_name[AO_NUM_ADC] = {
+       AO_ADC_SQ1_NAME,
+#ifdef AO_ADC_SQ2_NAME
+       AO_ADC_SQ2_NAME,
+#endif
+#ifdef AO_ADC_SQ3_NAME
+       AO_ADC_SQ3_NAME,
+#endif
+#ifdef AO_ADC_SQ4_NAME
+       AO_ADC_SQ4_NAME,
+#endif
+#ifdef AO_ADC_SQ5_NAME
+       AO_ADC_SQ5_NAME,
+#endif
+#ifdef AO_ADC_SQ6_NAME
+       AO_ADC_SQ6_NAME,
+#endif
+#ifdef AO_ADC_SQ7_NAME
+       AO_ADC_SQ7_NAME,
+#endif
+#ifdef AO_ADC_SQ8_NAME
+       AO_ADC_SQ8_NAME,
+#endif
+#ifdef AO_ADC_SQ9_NAME
+       AO_ADC_SQ9_NAME,
+#endif
+#ifdef AO_ADC_SQ10_NAME
+       AO_ADC_SQ10_NAME,
+#endif
+#ifdef AO_ADC_SQ11_NAME
+       AO_ADC_SQ11_NAME,
+#endif
+#ifdef AO_ADC_SQ12_NAME
+       AO_ADC_SQ12_NAME,
+#endif
+#ifdef AO_ADC_SQ13_NAME
+       AO_ADC_SQ13_NAME,
+#endif
+#ifdef AO_ADC_SQ14_NAME
+       AO_ADC_SQ14_NAME,
+#endif
+#ifdef AO_ADC_SQ15_NAME
+       AO_ADC_SQ15_NAME,
+#endif
+#ifdef AO_ADC_SQ16_NAME
+       AO_ADC_SQ16_NAME,
+#endif
+#ifdef AO_ADC_SQ17_NAME
+       AO_ADC_SQ17_NAME,
+#endif
+#ifdef AO_ADC_SQ18_NAME
+       AO_ADC_SQ18_NAME,
+#endif
+#ifdef AO_ADC_SQ19_NAME
+       AO_ADC_SQ19_NAME,
+#endif
+#ifdef AO_ADC_SQ20_NAME
+       AO_ADC_SQ20_NAME,
+#endif
+#ifdef AO_ADC_SQ21_NAME
+       #error "too many ADC names"
+#endif
+};
+#endif
+
+static void
+ao_adc_dump(void) 
+{
+       struct ao_data  packet;
+#ifndef AO_ADC_DUMP
+       uint8_t i;
+       int16_t *d;
+#endif
+
+       ao_data_get(&packet);
+#ifdef AO_ADC_DUMP
+       AO_ADC_DUMP(&packet);
+#else
+       printf("tick: %5u",  packet.tick);
+       d = (int16_t *) (&packet.adc);
+       for (i = 0; i < AO_NUM_ADC; i++) {
+#ifdef AO_ADC_SQ1_NAME
+               if (ao_adc_name[i])
+                       printf (" %s: %5d", ao_adc_name[i], d[i]);
+               else
+#endif
+                       printf (" %2d: %5d", i, d[i]);
+       }
+       printf("\n");
+#endif
+}
+
+const struct ao_cmds ao_adc_cmds[] = {
+       { ao_adc_dump,  "a\0Display current ADC values" },
+       { 0, NULL },
+};
+
+static inline void
+adc_pin_set(struct stm_gpio *gpio, int pin)
+{
+       ao_enable_port(gpio);
+       stm_gpio_conf(gpio, pin,
+                     STM_GPIO_CR_MODE_INPUT,
+                     STM_GPIO_CR_CNF_INPUT_ANALOG);
+}
+
+void
+ao_adc_init(void)
+{
+#ifdef AO_ADC_PIN0_PORT
+       adc_pin_set(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN);
+#endif
+#ifdef AO_ADC_PIN1_PORT
+       adc_pin_set(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN);
+#endif
+#ifdef AO_ADC_PIN2_PORT
+       adc_pin_set(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN);
+#endif
+#ifdef AO_ADC_PIN3_PORT
+       adc_pin_set(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN);
+#endif
+#ifdef AO_ADC_PIN4_PORT
+       adc_pin_set(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN);
+#endif
+#ifdef AO_ADC_PIN5_PORT
+       adc_pin_set(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN);
+#endif
+#ifdef AO_ADC_PIN6_PORT
+       adc_pin_set(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN);
+#endif
+#ifdef AO_ADC_PIN7_PORT
+       adc_pin_set(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN);
+#endif
+#ifdef AO_ADC_PIN8_PORT
+       adc_pin_set(AO_ADC_PIN8_PORT, AO_ADC_PIN8_PIN);
+#endif
+#ifdef AO_ADC_PIN9_PORT
+       adc_pin_set(AO_ADC_PIN9_PORT, AO_ADC_PIN9_PIN);
+#endif
+#ifdef AO_ADC_PIN10_PORT
+       adc_pin_set(AO_ADC_PIN10_PORT, AO_ADC_PIN10_PIN);
+#endif
+#ifdef AO_ADC_PIN11_PORT
+       adc_pin_set(AO_ADC_PIN11_PORT, AO_ADC_PIN11_PIN);
+#endif
+#ifdef AO_ADC_PIN12_PORT
+       adc_pin_set(AO_ADC_PIN12_PORT, AO_ADC_PIN12_PIN);
+#endif
+#ifdef AO_ADC_PIN13_PORT
+       adc_pin_set(AO_ADC_PIN13_PORT, AO_ADC_PIN13_PIN);
+#endif
+#ifdef AO_ADC_PIN14_PORT
+       adc_pin_set(AO_ADC_PIN14_PORT, AO_ADC_PIN14_PIN);
+#endif
+#ifdef AO_ADC_PIN15_PORT
+       adc_pin_set(AO_ADC_PIN15_PORT, AO_ADC_PIN15_PIN);
+#endif
+#ifdef AO_ADC_PIN16_PORT
+       adc_pin_set(AO_ADC_PIN16_PORT, AO_ADC_PIN16_PIN);
+#endif
+#ifdef AO_ADC_PIN17_PORT
+       adc_pin_set(AO_ADC_PIN17_PORT, AO_ADC_PIN17_PIN);
+#endif
+#ifdef AO_ADC_PIN18_PORT
+       adc_pin_set(AO_ADC_PIN18_PORT, AO_ADC_PIN18_PIN);
+#endif
+#ifdef AO_ADC_PIN19_PORT
+       adc_pin_set(AO_ADC_PIN19_PORT, AO_ADC_PIN19_PIN);
+#endif
+#ifdef AO_ADC_PIN20_PORT
+       adc_pin_set(AO_ADC_PIN20_PORT, AO_ADC_PIN20_PIN);
+#endif
+#ifdef AO_ADC_PIN21_PORT
+       adc_pin_set(AO_ADC_PIN21_PORT, AO_ADC_PIN21_PIN);
+#endif
+#ifdef AO_ADC_PIN22_PORT
+       adc_pin_set(AO_ADC_PIN22_PORT, AO_ADC_PIN22_PIN);
+#endif
+#ifdef AO_ADC_PIN23_PORT
+       adc_pin_set(AO_ADC_PIN23_PORT, AO_ADC_PIN23_PIN);
+#endif
+#ifdef AO_ADC_PIN24_PORT
+       #error "Too many ADC ports"
+#endif
+
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_ADC1EN);
+
+       /* Turn off ADC during configuration */
+       stm_adc1.cr2 = 0;
+
+       stm_adc1.cr1 = ((0 << STM_ADC_CR1_AWDEN ) |
+                      (0 << STM_ADC_CR1_JAWDEN ) |
+                      (STM_ADC_CR1_DUALMOD_INDEPENDENT << STM_ADC_CR1_DUALMOD ) |
+                      (0 << STM_ADC_CR1_DISCNUM ) |
+                      (0 << STM_ADC_CR1_JDISCEN ) |
+                      (0 << STM_ADC_CR1_DISCEN ) |
+                      (0 << STM_ADC_CR1_JAUTO ) |
+                      (0 << STM_ADC_CR1_AWDSGL ) |
+                      (1 << STM_ADC_CR1_SCAN ) |
+                      (0 << STM_ADC_CR1_JEOCIE ) |
+                      (0 << STM_ADC_CR1_AWDIE ) |
+                      (0 << STM_ADC_CR1_EOCIE ) |
+                      (0 << STM_ADC_CR1_AWDCH ));
+
+       /* 384 cycle sample time for everyone */
+       stm_adc1.smpr1 = 0x3ffff;
+       stm_adc1.smpr2 = 0x3fffffff;
+
+       stm_adc1.sqr1 = ((AO_NUM_ADC - 1) << 20);
+#if AO_NUM_ADC > 0
+       stm_adc1.sqr3 |= (AO_ADC_SQ1 << 0);
+#endif
+#if AO_NUM_ADC > 1
+       stm_adc1.sqr3 |= (AO_ADC_SQ2 << 5);
+#endif
+#if AO_NUM_ADC > 2
+       stm_adc1.sqr3 |= (AO_ADC_SQ3 << 10);
+#endif
+#if AO_NUM_ADC > 3
+       stm_adc1.sqr3 |= (AO_ADC_SQ4 << 15);
+#endif
+#if AO_NUM_ADC > 4
+       stm_adc1.sqr3 |= (AO_ADC_SQ5 << 20);
+#endif
+#if AO_NUM_ADC > 5
+       stm_adc1.sqr3 |= (AO_ADC_SQ6 << 25);
+#endif
+#if AO_NUM_ADC > 6
+       stm_adc1.sqr2 |= (AO_ADC_SQ7 << 0);
+#endif
+#if AO_NUM_ADC > 7
+       stm_adc1.sqr2 |= (AO_ADC_SQ8 << 5);
+#endif
+#if AO_NUM_ADC > 8
+       stm_adc1.sqr2 |= (AO_ADC_SQ9 << 10);
+#endif
+#if AO_NUM_ADC > 9
+       stm_adc1.sqr2 |= (AO_ADC_SQ10 << 15);
+#endif
+#if AO_NUM_ADC > 10
+       stm_adc1.sqr2 |= (AO_ADC_SQ11 << 20);
+#endif
+#if AO_NUM_ADC > 11
+       stm_adc1.sqr2 |= (AO_ADC_SQ12 << 25);
+#endif
+#if AO_NUM_ADC > 12
+       stm_adc1.sqr1 |= (AO_ADC_SQ13 << 0);
+#endif
+#if AO_NUM_ADC > 13
+       stm_adc1.sqr1 |= (AO_ADC_SQ14 << 5);
+#endif
+#if AO_NUM_ADC > 14
+       stm_adc1.sqr1 |= (AO_ADC_SQ15 << 10);
+#endif
+#if AO_NUM_ADC > 15
+       stm_adc1.sqr1 |= (AO_ADC_SQ16 << 15);
+#endif
+#if AO_NUM_ADC > 15
+#error "too many ADC channels"
+#endif
+
+#ifndef HAS_ADC_TEMP
+#error Please define HAS_ADC_TEMP
+#endif
+#if HAS_ADC_TEMP
+       stm_adc1.cr2 |= ((1 << STM_ADC_CR2_TSVREFE));
+#endif
+
+       /* Clear any stale status bits */
+       stm_adc1.sr = 0;
+
+       ao_dma_alloc(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+
+       ao_cmd_register(&ao_adc_cmds[0]);
+
+       ao_adc_ready = 1;
+}
diff --git a/src/stm32f1/ao_arch.h b/src/stm32f1/ao_arch.h
new file mode 100644 (file)
index 0000000..b58e33f
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright © 2023 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_ARCH_H_
+#define _AO_ARCH_H_
+
+#include <stm32f1.h>
+
+#ifndef AO_STACK_SIZE
+#define AO_STACK_SIZE  512
+#endif
+
+#define AO_PORT_TYPE   uint16_t
+
+#define ao_arch_naked_declare  __attribute__((naked))
+#define ao_arch_naked_define
+#define __interrupt(n)
+#define __at(n)
+
+#define ao_arch_nop()          asm("nop")
+
+#define ao_arch_interrupt(n)   /* nothing */
+
+#define AO_ROMCONFIG_SYMBOL __attribute__((section(".init.1"))) const
+#define AO_USBCONFIG_SYMBOL __attribute__((section(".init.2"))) const
+
+#define AO_SYSTICK     (AO_HCLK / 8)
+
+#if AO_NONMASK_INTERRUPT
+#define AO_STM_NVIC_NONMASK_PRIORITY   0x00
+
+/* Set the basepri register to this value to mask all
+ * non-maskable priorities
+ */
+#define AO_STM_NVIC_BASEPRI_MASK       0x10
+#endif
+
+#define AO_STM_NVIC_HIGH_PRIORITY      0x40
+#define AO_STM_NVIC_MED_PRIORITY       0x80
+#define AO_STM_NVIC_LOW_PRIORITY       0xC0
+#define AO_STM_NVIC_CLOCK_PRIORITY     0xf0
+
+#define AO_PCLK1       AO_APB1CLK
+#define AO_PCLK2       AO_APB2CLK
+
+#if AO_RCC_CFGR_PPRE1_DIV == STM_RCC_CFGR_PPRE1_DIV_1
+#define AO_TIM23467_CLK                AO_APB1CLK
+#else
+#define AO_TIM23467_CLK                (2 * AO_APB1CLK)
+#endif
+
+/* ADC maximum reported value */
+#define AO_ADC_MAX                     4095
+
+#define AO_BOOT_APPLICATION_BASE       ((uint32_t *) 0x08001000)
+#define AO_BOOT_APPLICATION_BOUND      ((uint32_t *) (0x08000000 + stm_flash_size()))
+#define AO_BOOT_LOADER_BASE            ((uint32_t *) 0x08000000)
+#define HAS_BOOT_LOADER                        1
+
+#endif /* _AO_ARCH_H_ */
diff --git a/src/stm32f1/ao_arch_funcs.h b/src/stm32f1/ao_arch_funcs.h
new file mode 100644 (file)
index 0000000..7004eb4
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ * Copyright © 2023 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_ARCH_FUNCS_H_
+#define _AO_ARCH_FUNCS_H_
+
+#define AO_EXTI_MODE_RISING    1
+#define AO_EXTI_MODE_FALLING   2
+#define AO_EXTI_MODE_PULL_NONE 0
+#define AO_EXTI_MODE_PULL_UP   4
+#define AO_EXTI_MODE_PULL_DOWN 8
+#define AO_EXTI_PRIORITY_LOW   16
+#define AO_EXTI_PRIORITY_MED   0
+#define AO_EXTI_PRIORITY_HIGH  32
+#define AO_EXTI_PIN_NOCONFIGURE        64
+
+static inline void
+ao_enable_port(struct stm_gpio *port)
+{
+       if ((port) == &stm_gpioa)
+               stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_IOPAEN);
+       else if ((port) == &stm_gpiob)
+               stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_IOPBEN);
+       else if ((port) == &stm_gpioc)
+               stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_IOPCEN);
+       else if ((port) == &stm_gpiod)
+               stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_IOPDEN);
+       else if ((port) == &stm_gpioe)
+               stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_IOPEEN);
+}
+
+static inline void
+ao_disable_port(struct stm_gpio *port)
+{
+       if ((port) == &stm_gpioa)
+               stm_rcc.apb2enr &= ~(1UL << STM_RCC_APB2ENR_IOPAEN);
+       else if ((port) == &stm_gpiob)
+               stm_rcc.apb2enr &= ~(1UL << STM_RCC_APB2ENR_IOPBEN);
+       else if ((port) == &stm_gpioc)
+               stm_rcc.apb2enr &= ~(1UL << STM_RCC_APB2ENR_IOPCEN);
+       else if ((port) == &stm_gpiod)
+               stm_rcc.apb2enr &= ~(1UL << STM_RCC_APB2ENR_IOPDEN);
+       else if ((port) == &stm_gpioe)
+               stm_rcc.apb2enr &= ~(1UL << STM_RCC_APB2ENR_IOPEEN);
+}
+
+#define ao_gpio_set(port, bit, v) stm_gpio_set(port, bit, v)
+
+#define ao_gpio_get(port, bit) stm_gpio_get(port, bit)
+
+#define ao_gpio_set_bits(port, bits) stm_gpio_set_bits(port, bits)
+
+#define ao_gpio_set_mask(port, bits, mask) stm_gpio_set_mask(port, bits, mask)
+
+#define ao_gpio_clr_bits(port, bits) stm_gpio_clr_bits(port, bits);
+
+#define ao_gpio_get_all(port) stm_gpio_get_all(port)
+
+static inline void
+ao_enable_output(struct stm_gpio *port, int bit, uint8_t v)
+{
+       ao_enable_port(port);
+       ao_gpio_set(port, bit, v);
+       stm_gpio_conf(port, bit,
+                     STM_GPIO_CR_MODE_OUTPUT_10MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_PUSH_PULL);
+}
+
+static inline void
+ao_gpio_set_mode(struct stm_gpio *port, int bit, int mode)
+{
+       uint8_t cnf;
+
+       if (mode == AO_EXTI_MODE_PULL_NONE)
+               cnf = STM_GPIO_CR_CNF_INPUT_FLOATING;
+       else {
+               cnf = STM_GPIO_CR_CNF_INPUT_PULL;
+               ao_gpio_set(port, bit, mode == AO_EXTI_MODE_PULL_UP);
+       }
+       stm_gpio_conf(port, bit,
+                     STM_GPIO_CR_MODE_INPUT,
+                     cnf);
+}
+
+static inline void
+ao_enable_input(struct stm_gpio *port, int bit, int mode)
+{
+       ao_enable_port(port);
+       ao_gpio_set_mode(port, bit, mode);
+}
+
+static inline void
+ao_enable_cs(struct stm_gpio *port, int bit)
+{
+       ao_enable_output(port, bit, 1);
+}
+
+#if USE_SERIAL_1_SW_FLOW || USE_SERIAL_2_SW_FLOW || USE_SERIAL_3_SW_FLOW
+#define HAS_SERIAL_SW_FLOW 1
+#else
+#define HAS_SERIAL_SW_FLOW 0
+#endif
+
+#if USE_SERIAL_1_FLOW && !USE_SERIAL_1_SW_FLOW || USE_SERIAL_2_FLOW && !USE_SERIAL_2_SW_FLOW || USE_SERIAL_3_FLOW && !USE_SERIAL_3_SW_FLOW
+#define HAS_SERIAL_HW_FLOW 1
+#else
+#define HAS_SERIAL_HW_FLOW 0
+#endif
+
+/* ao_serial_stm.c */
+struct ao_stm_usart {
+       struct ao_fifo          rx_fifo;
+       struct ao_fifo          tx_fifo;
+       struct stm_usart        *reg;
+       uint8_t                 tx_running;
+       uint8_t                 draining;
+#if HAS_SERIAL_SW_FLOW
+       /* RTS - 0 if we have FIFO space, 1 if not
+        * CTS - 0 if we can send, 0 if not
+        */
+       struct stm_gpio         *gpio_rts;
+       struct stm_gpio         *gpio_cts;
+       uint8_t                 pin_rts;
+       uint8_t                 pin_cts;
+       uint8_t                 rts;
+#endif
+};
+
+void
+ao_debug_out(char c);
+
+#if HAS_SERIAL_1
+extern struct ao_stm_usart     ao_stm_usart1;
+#endif
+
+#if HAS_SERIAL_2
+extern struct ao_stm_usart     ao_stm_usart2;
+#endif
+
+#if HAS_SERIAL_3
+extern struct ao_stm_usart     ao_stm_usart3;
+#endif
+
+#define ARM_PUSH32(stack, val) (*(--(stack)) = (val))
+
+typedef uint32_t       ao_arch_irq_t;
+
+static inline void
+ao_arch_block_interrupts(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+       asm("msr basepri,%0" : : "r" (AO_STM_NVIC_BASEPRI_MASK));
+#else
+       asm("cpsid i");
+#endif
+}
+
+static inline void
+ao_arch_release_interrupts(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+       asm("msr basepri,%0" : : "r" (0x0));
+#else
+       asm("cpsie i");
+#endif
+}
+
+static inline uint32_t
+ao_arch_irqsave(void) {
+       uint32_t        val;
+#ifdef AO_NONMASK_INTERRUPTS
+       asm("mrs %0,basepri" : "=r" (val));
+#else
+       asm("mrs %0,primask" : "=r" (val));
+#endif
+       ao_arch_block_interrupts();
+       return val;
+}
+
+static inline void
+ao_arch_irqrestore(uint32_t basepri) {
+#ifdef AO_NONMASK_INTERRUPTS
+       asm("msr basepri,%0" : : "r" (basepri));
+#else
+       asm("msr primask,%0" : : "r" (basepri));
+#endif
+}
+
+static inline void
+ao_arch_memory_barrier(void) {
+       asm volatile("" ::: "memory");
+}
+
+static inline void
+ao_arch_irq_check(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+       uint32_t        basepri;
+       asm("mrs %0,basepri" : "=r" (basepri));
+       if (basepri == 0)
+               ao_panic(AO_PANIC_IRQ);
+#else
+       uint32_t        primask;
+       asm("mrs %0,primask" : "=r" (primask));
+       if ((primask & 1) == 0)
+               ao_panic(AO_PANIC_IRQ);
+#endif
+}
+
+#if HAS_TASK
+static inline void
+ao_arch_init_stack(struct ao_task *task, uint32_t *sp, void *start)
+{
+       uint32_t        a = (uint32_t) start;
+       int             i;
+
+       /* Return address (goes into LR) */
+       ARM_PUSH32(sp, a);
+
+       /* Clear register values r0-r12 */
+       i = 13;
+       while (i--)
+               ARM_PUSH32(sp, 0);
+
+       /* APSR */
+       ARM_PUSH32(sp, 0);
+
+       /* BASEPRI with interrupts enabled */
+       ARM_PUSH32(sp, 0);
+
+       task->sp32 = sp;
+}
+
+static inline void ao_arch_save_regs(void) {
+       /* Save general registers */
+       asm("push {r0-r12,lr}\n");
+
+       /* Save APSR */
+       asm("mrs r0,apsr");
+       asm("push {r0}");
+
+#ifdef AO_NONMASK_INTERRUPTS
+       /* Save BASEPRI */
+       asm("mrs r0,basepri");
+#else
+       /* Save PRIMASK */
+       asm("mrs r0,primask");
+#endif
+       asm("push {r0}");
+}
+
+static inline void ao_arch_save_stack(void) {
+       uint32_t        *sp;
+       asm("mov %0,sp" : "=&r" (sp) );
+       ao_cur_task->sp32 = (sp);
+}
+
+static inline void ao_arch_restore_stack(void) {
+       /* Switch stacks */
+       asm("mov sp, %0" : : "r" (ao_cur_task->sp32) );
+
+#ifdef AO_NONMASK_INTERRUPTS
+       /* Restore BASEPRI */
+       asm("pop {r0}");
+       asm("msr basepri,r0");
+#else
+       /* Restore PRIMASK */
+       asm("pop {r0}");
+       asm("msr primask,r0");
+#endif
+
+       /* Restore APSR */
+       asm("pop {r0}");
+       asm("msr apsr_nczvq,r0");
+
+       /* Restore general registers */
+       asm("pop {r0-r12,lr}\n");
+
+       /* Return to calling function */
+       asm("bx lr");
+}
+#define HAS_ARCH_START_SCHEDULER       1
+
+static inline void ao_arch_start_scheduler(void) {
+       uint32_t        sp;
+       uint32_t        control;
+
+       asm("mrs %0,msp" : "=&r" (sp));
+       asm("msr psp,%0" : : "r" (sp));
+       asm("mrs %0,control" : "=r" (control));
+       control |= (1 << 1);
+       asm("msr control,%0" : : "r" (control));
+       asm("isb");
+}
+
+#define ao_arch_isr_stack()
+
+#endif /* HAS_TASK */
+
+static inline void
+ao_arch_wait_interrupt(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+       asm(
+           "dsb\n"                     /* Serialize data */
+           "isb\n"                     /* Serialize instructions */
+           "cpsid i\n"                 /* Block all interrupts */
+           "msr basepri,%0\n"          /* Allow all interrupts through basepri */
+           "wfi\n"                     /* Wait for an interrupt */
+           "cpsie i\n"                 /* Allow all interrupts */
+           "msr basepri,%1\n"          /* Block interrupts through basepri */
+           : : "r" (0), "r" (AO_STM_NVIC_BASEPRI_MASK));
+#else
+       asm("\twfi\n");
+       ao_arch_release_interrupts();
+       ao_arch_block_interrupts();
+#endif
+}
+
+#define ao_arch_critical(b) do {                       \
+               uint32_t __mask = ao_arch_irqsave();    \
+               do { b } while (0);                     \
+               ao_arch_irqrestore(__mask);             \
+       } while (0)
+
+#define ao_arch_reboot() \
+       (stm_scb.aircr = ((STM_SCB_AIRCR_VECTKEY_KEY << STM_SCB_AIRCR_VECTKEY) | \
+                         (1 << STM_SCB_AIRCR_SYSRESETREQ)))
+
+/* ao_dma_stm.c
+ */
+
+extern uint8_t ao_dma_done[STM_NUM_DMA];
+
+void
+ao_dma_set_transfer(uint8_t            index,
+                   volatile void       *peripheral,
+                   void                *memory,
+                   uint16_t            count,
+                   uint32_t            ccr);
+
+void
+ao_dma_mutex_get(uint8_t index);
+
+void
+ao_dma_mutex_put(uint8_t index);
+
+void
+ao_dma_set_isr(uint8_t index, void (*isr)(int index));
+
+void
+ao_dma_start(uint8_t index);
+
+void
+ao_dma_done_transfer(uint8_t index);
+
+void
+ao_dma_alloc(uint8_t index);
+
+void
+ao_dma_init(void);
+
+/* ao_spi_stm.c
+ */
+
+#define AO_SPI_CPOL_BIT                4
+#define AO_SPI_CPHA_BIT                5
+
+#define AO_SPI_CONFIG_1                0x00
+#define AO_SPI_1_CONFIG_PA5_PA6_PA7    AO_SPI_CONFIG_1
+#define AO_SPI_2_CONFIG_PB13_PB14_PB15 AO_SPI_CONFIG_1
+
+#define AO_SPI_CONFIG_2                0x04
+#define AO_SPI_1_CONFIG_PB3_PB4_PB5    AO_SPI_CONFIG_2
+#define AO_SPI_2_CONFIG_PD1_PD3_PD4    AO_SPI_CONFIG_2
+
+#define AO_SPI_CONFIG_3                0x08
+#define AO_SPI_1_CONFIG_PE13_PE14_PE15 AO_SPI_CONFIG_3
+
+#define AO_SPI_CONFIG_NONE     0x0c
+
+#define AO_SPI_INDEX_MASK      0x01
+#define AO_SPI_CONFIG_MASK     0x0c
+
+#define AO_SPI_1_PA5_PA6_PA7   (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PA5_PA6_PA7)
+#define AO_SPI_1_PB3_PB4_PB5   (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PB3_PB4_PB5)
+#define AO_SPI_1_PE13_PE14_PE15        (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PE13_PE14_PE15)
+
+#define AO_SPI_2_PB13_PB14_PB15        (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PB13_PB14_PB15)
+#define AO_SPI_2_PD1_PD3_PD4   (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PD1_PD3_PD4)
+
+#define AO_SPI_INDEX(id)       ((id) & AO_SPI_INDEX_MASK)
+#define AO_SPI_CONFIG(id)      ((id) & AO_SPI_CONFIG_MASK)
+#define AO_SPI_PIN_CONFIG(id)  ((id) & (AO_SPI_INDEX_MASK | AO_SPI_CONFIG_MASK))
+#define AO_SPI_CPOL(id)                ((uint32_t) (((id) >> AO_SPI_CPOL_BIT) & 1))
+#define AO_SPI_CPHA(id)                ((uint32_t) (((id) >> AO_SPI_CPHA_BIT) & 1))
+
+#define AO_SPI_MAKE_MODE(pol,pha)      (((pol) << AO_SPI_CPOL_BIT) | ((pha) << AO_SPI_CPHA_BIT))
+#define AO_SPI_MODE_0          AO_SPI_MAKE_MODE(0,0)
+#define AO_SPI_MODE_1          AO_SPI_MAKE_MODE(0,1)
+#define AO_SPI_MODE_2          AO_SPI_MAKE_MODE(1,0)
+#define AO_SPI_MODE_3          AO_SPI_MAKE_MODE(1,1)
+
+/* SPI1 is on APB2, SPI2 is on APB1 */
+#define AO_SPI_FREQ(bus, div)  ((AO_SPI_INDEX(bus) == 0 ? AO_APB2CLK : AO_APB1CLK) / (div))
+
+static inline uint32_t
+ao_spi_speed(int num, uint32_t hz)
+{
+       if (hz >= AO_SPI_FREQ(num, 2)) return STM_SPI_CR1_BR_PCLK_2;
+       if (hz >= AO_SPI_FREQ(num, 4)) return STM_SPI_CR1_BR_PCLK_4;
+       if (hz >= AO_SPI_FREQ(num, 8)) return STM_SPI_CR1_BR_PCLK_8;
+       if (hz >= AO_SPI_FREQ(num, 16)) return STM_SPI_CR1_BR_PCLK_16;
+       if (hz >= AO_SPI_FREQ(num, 32)) return STM_SPI_CR1_BR_PCLK_32;
+       if (hz >= AO_SPI_FREQ(num, 64)) return STM_SPI_CR1_BR_PCLK_64;
+       if (hz >= AO_SPI_FREQ(num, 128)) return STM_SPI_CR1_BR_PCLK_128;
+       return STM_SPI_CR1_BR_PCLK_256;
+}
+
+uint8_t
+ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id);
+
+void
+ao_spi_get(uint8_t spi_index, uint32_t speed);
+
+void
+ao_spi_put(uint8_t spi_index);
+
+void
+ao_spi_put_pins(uint8_t spi_index);
+
+void
+ao_spi_send(const void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_send_sync(const void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_start_bytes(uint8_t spi_index);
+
+void
+ao_spi_stop_bytes(uint8_t spi_index);
+
+static inline void
+ao_spi_send_byte(uint8_t byte, uint8_t spi_index)
+{
+       struct stm_spi  *stm_spi;
+
+       switch (AO_SPI_INDEX(spi_index)) {
+       case 0:
+               stm_spi = &stm_spi1;
+               break;
+       case 1:
+               stm_spi = &stm_spi2;
+               break;
+       }
+
+       while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE)))
+               ;
+       stm_spi->dr = byte;
+       while (!(stm_spi->sr & (1 << STM_SPI_SR_RXNE)))
+               ;
+       (void) stm_spi->dr;
+}
+
+static inline uint8_t
+ao_spi_recv_byte(uint8_t spi_index)
+{
+       struct stm_spi  *stm_spi;
+
+       switch (AO_SPI_INDEX(spi_index)) {
+       case 0:
+               stm_spi = &stm_spi1;
+               break;
+       case 1:
+               stm_spi = &stm_spi2;
+               break;
+       }
+
+       while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE)))
+               ;
+       stm_spi->dr = 0xff;
+       while (!(stm_spi->sr & (1 << STM_SPI_SR_RXNE)))
+               ;
+       return (uint8_t) stm_spi->dr;
+}
+
+void
+ao_spi_recv(void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_init(void);
+
+#define ao_spi_init_cs(port, mask) do {                                        \
+               uint8_t __bit__;                                        \
+               for (__bit__ = 0; __bit__ < 32; __bit__++) {            \
+                       if (mask & (1 << __bit__))                      \
+                               ao_enable_output(port, __bit__, 1); \
+               }                                                       \
+       } while (0)
+
+#define ao_spi_set_cs(reg,mask) ((reg)->bsrr = ((uint32_t) (mask)) << 16)
+#define ao_spi_clr_cs(reg,mask) ((reg)->bsrr = (mask))
+
+#define ao_spi_get_mask(reg,mask,bus, speed) do {              \
+               ao_spi_get(bus, speed);                         \
+               ao_spi_set_cs(reg,mask);                        \
+       } while (0)
+
+static inline uint8_t
+ao_spi_try_get_mask(struct stm_gpio *reg, uint16_t mask, uint8_t bus, uint32_t speed, uint8_t task_id)
+{
+       if (!ao_spi_try_get(bus, speed, task_id))
+               return 0;
+       ao_spi_set_cs(reg, mask);
+       return 1;
+}
+
+#define ao_spi_put_mask(reg,mask,bus) do {     \
+               ao_spi_clr_cs(reg,mask);        \
+               ao_spi_put(bus);                \
+       } while (0)
+
+#define ao_spi_get_bit(reg,bit,bus,speed) ao_spi_get_mask(reg,1<<(bit),bus,speed)
+#define ao_spi_put_bit(reg,bit,bus) ao_spi_put_mask(reg,1<<(bit),bus)
+
+void
+ao_i2c_get(uint8_t i2c_index);
+
+uint8_t
+ao_i2c_start(uint8_t i2c_index, uint16_t address);
+
+void
+ao_i2c_put(uint8_t i2c_index);
+
+uint8_t
+ao_i2c_send(void *block, uint16_t len, uint8_t i2c_index, uint8_t stop);
+
+uint8_t
+ao_i2c_recv(void *block, uint16_t len, uint8_t i2c_index, uint8_t stop);
+
+void
+ao_i2c_init(void);
+
+#endif /* _AO_ARCH_FUNCS_H_ */
diff --git a/src/stm32f1/ao_beep_stm.c b/src/stm32f1/ao_beep_stm.c
new file mode 100644 (file)
index 0000000..02c8d91
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_beep.h"
+
+#if BEEPER_TIMER == 2
+#define stm_beeper     stm_tim2
+#define RCC_BEEPER     STM_RCC_APB1ENR_TIM2EN
+#elif BEEPER_TIMER == 3
+#define stm_beeper     stm_tim3
+#define RCC_BEEPER     STM_RCC_APB1ENR_TIM3EN
+#elif BEEPER_TIMER == 4
+#define stm_beeper     stm_tim4
+#define RCC_BEEPER     STM_RCC_APB1ENR_TIM4EN
+#else
+#error BEEPER_TIMER must be 2, 3 or 4
+#endif
+
+void
+ao_beep(uint8_t beep)
+{
+       if (beep == 0) {
+               stm_beeper.cr1 = 0;
+               stm_rcc.apb1enr &= ~(1UL << RCC_BEEPER);
+               stm_gpio_conf(BEEPER_PORT, BEEPER_PIN,
+                             STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                             STM_GPIO_CR_CNF_OUTPUT_PUSH_PULL);
+       } else {
+               stm_gpio_conf(BEEPER_PORT, BEEPER_PIN,
+                             STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                             STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+               stm_rcc.apb1enr |= (1UL << RCC_BEEPER);
+
+               stm_beeper.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
+                               (STM_TIM234_CR2_MMS_RESET << STM_TIM234_CR2_MMS) |
+                               (0 << STM_TIM234_CR2_CCDS));
+
+               /* Set prescaler to match cc1111 clocks
+                */
+               stm_beeper.psc = AO_TIM23467_CLK / 750000;
+
+               /* 1. Select the counter clock (internal, external, prescaler).
+                *
+                * Setting SMCR to zero means use the internal clock
+                */
+
+               stm_beeper.smcr = 0;
+
+               /* 2. Write the desired data in the TIMx_ARR and TIMx_CCRx registers. */
+               stm_beeper.arr = beep;
+#if BEEPER_CHANNEL == 1
+               stm_beeper.ccr1 = beep;
+#elif BEEPER_CHANNEL == 2
+               stm_beeper.ccr2 = beep;
+#elif BEEPER_CHANNEL == 3
+               stm_beeper.ccr3 = beep;
+#elif BEEPER_CHANNEL == 4
+               stm_beeper.ccr4 = beep;
+#else
+#error invalid BEEPER_CHANNEL
+#endif
+
+               /* 3. Set the CCxIE and/or CCxDE bits if an interrupt and/or a
+                * DMA request is to be generated.
+                */
+               /* don't want this */
+
+               /* 4. Select the output mode. For example, you must write
+                *  OCxM=011, OCxPE=0, CCxP=0 and CCxE=1 to toggle OCx output
+                *  pin when CNT matches CCRx, CCRx preload is not used, OCx
+                *  is enabled and active high.
+                */
+
+#define OC1M   (BEEPER_CHANNEL == 1 ? STM_TIM234_CCMR1_OC1M_TOGGLE : STM_TIM234_CCMR1_OC1M_FROZEN)
+#define OC2M   (BEEPER_CHANNEL == 2 ? STM_TIM234_CCMR1_OC2M_TOGGLE : STM_TIM234_CCMR1_OC2M_FROZEN)
+#define OC3M   (BEEPER_CHANNEL == 3 ? STM_TIM234_CCMR2_OC3M_TOGGLE : STM_TIM234_CCMR2_OC3M_FROZEN)
+#define OC4M   (BEEPER_CHANNEL == 4 ? STM_TIM234_CCMR2_OC4M_TOGGLE : STM_TIM234_CCMR2_OC4M_FROZEN)
+
+#define CCER(n)        (BEEPER_CHANNEL == (n) ? 1 : 0)
+
+#if BEEPER_CHANNEL == 1 || BEEPER_CHANNEL == 2
+               stm_beeper.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
+                                   (OC2M << STM_TIM234_CCMR1_OC2M) |
+                                   (0 << STM_TIM234_CCMR1_OC2PE) |
+                                   (0 << STM_TIM234_CCMR1_OC2FE) |
+                                   (STM_TIM234_CCMR1_CC2S_OUTPUT << STM_TIM234_CCMR1_CC2S) |
+
+                                   (0 << STM_TIM234_CCMR1_OC1CE) |
+                                   (OC1M << STM_TIM234_CCMR1_OC1M) |
+                                   (0 << STM_TIM234_CCMR1_OC1PE) |
+                                   (0 << STM_TIM234_CCMR1_OC1FE) |
+                                   (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
+#elif BEEPER_CHANNEL == 3 || BEEPER_CHANNEL == 4
+               stm_beeper.ccmr2 = ((0 << STM_TIM234_CCMR2_OC4CE) |
+                                   (OC4M << STM_TIM234_CCMR2_OC4M) |
+                                   (0 << STM_TIM234_CCMR2_OC4PE) |
+                                   (0 << STM_TIM234_CCMR2_OC4FE) |
+                                   (STM_TIM234_CCMR2_CC4S_OUTPUT << STM_TIM234_CCMR2_CC4S) |
+
+                                   (0 << STM_TIM234_CCMR2_OC3CE) |
+                                   (OC3M << STM_TIM234_CCMR2_OC3M) |
+                                   (0 << STM_TIM234_CCMR2_OC3PE) |
+                                   (0 << STM_TIM234_CCMR2_OC3FE) |
+                                   (STM_TIM234_CCMR2_CC3S_OUTPUT << STM_TIM234_CCMR2_CC3S));
+#else
+#error invalid BEEPER_CHANNEL
+#endif
+               stm_beeper.ccer = ((0 << STM_TIM234_CCER_CC4NP) |
+                                  (0 << STM_TIM234_CCER_CC4P) |
+                                  (CCER(4) << STM_TIM234_CCER_CC4E) |
+                                  (0 << STM_TIM234_CCER_CC3NP) |
+                                  (0 << STM_TIM234_CCER_CC3P) |
+                                  (CCER(3) << STM_TIM234_CCER_CC3E) |
+                                  (0 << STM_TIM234_CCER_CC2NP) |
+                                  (0 << STM_TIM234_CCER_CC2P) |
+                                  (CCER(2) << STM_TIM234_CCER_CC2E) |
+                                  (0 << STM_TIM234_CCER_CC1NP) |
+                                  (0 << STM_TIM234_CCER_CC1P) |
+                                  (CCER(1) << STM_TIM234_CCER_CC1E));
+
+               /* 5. Enable the counter by setting the CEN bit in the TIMx_CR1 register. */
+
+               stm_beeper.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+                               (0 << STM_TIM234_CR1_ARPE) |
+                               (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+                               (0 << STM_TIM234_CR1_DIR) |
+                               (0 << STM_TIM234_CR1_OPM) |
+                               (0 << STM_TIM234_CR1_URS) |
+                               (0 << STM_TIM234_CR1_UDIS) |
+                               (1 << STM_TIM234_CR1_CEN));
+
+               /* Update the values */
+               stm_beeper.egr = (1 << STM_TIM234_EGR_UG);
+       }
+}
+
+void
+ao_beep_for(uint8_t beep, AO_TICK_TYPE ticks)
+{
+       ao_beep(beep);
+       ao_delay(ticks);
+       ao_beep(0);
+}
+
+void
+ao_beep_init(void)
+{
+#if BEEPER_TIMER == 2
+       if (BEEPER_PORT == &stm_gpioa) {
+               switch (BEEPER_PIN) {
+               case 0:
+               case 1:
+               case 2:
+               case 3:
+                       stm_set_afio_mapr(STM_AFIO_MAPR_TIM2_REMAP,
+                                         STM_AFIO_MAPR_TIM2_REMAP_PA0_PA1_PA2_PA3,
+                                         STM_AFIO_MAPR_TIM2_REMAP_MASK);
+                       break;
+               default:
+                       ao_panic(AO_PANIC_CRASH);
+                       break;
+               }
+       }
+#elif BEEPER_TIMER == 3
+       if (BEEPER_PORT == &stm_gpioa) {
+               switch (BEEPER_PIN) {
+               case 6:
+               case 7:
+                       stm_set_afio_mapr(STM_AFIO_MAPR_TIM3_REMAP,
+                                         STM_AFIO_MAPR_TIM3_REMAP_PA6_PA7_PB0_PB1,
+                                         STM_AFIO_MAPR_TIM3_REMAP_MASK);
+                       break;
+               default:
+                       ao_panic(AO_PANIC_CRASH);
+                       break;
+               }
+       } else if (BEEPER_PORT == &stm_gpiob) {
+               switch (BEEPER_PIN) {
+               case 4:
+               case 5:
+               case 0:
+               case 1:
+                       stm_set_afio_mapr(STM_AFIO_MAPR_TIM3_REMAP,
+                                         STM_AFIO_MAPR_TIM3_REMAP_PB4_PB5_PB0_PB1,
+                                         STM_AFIO_MAPR_TIM3_REMAP_MASK);
+                       break;
+               default:
+                       ao_panic(AO_PANIC_CRASH);
+                       break;
+               }
+       } else if (BEEPER_PORT == &stm_gpioc) {
+               switch (BEEPER_PIN) {
+               case 6:
+               case 7:
+               case 8:
+               case 9:
+                       stm_set_afio_mapr(STM_AFIO_MAPR_TIM3_REMAP,
+                                         STM_AFIO_MAPR_TIM3_REMAP_PC6_PC7_PC8_PC9,
+                                         STM_AFIO_MAPR_TIM3_REMAP_MASK);
+                       break;
+               default:
+                       ao_panic(AO_PANIC_CRASH);
+                       break;
+               }
+       }
+#elif BEEPER_TIMER == 4
+       ao_panic(AO_PANIC_CRASH);
+#endif
+       ao_enable_output(BEEPER_PORT, BEEPER_PIN, 0);
+
+       /* Leave the timer off until requested */
+       stm_rcc.apb1enr &= ~(1UL << RCC_BEEPER);
+}
diff --git a/src/stm32f1/ao_boot_chain.c b/src/stm32f1/ao_boot_chain.c
new file mode 100644 (file)
index 0000000..d91afda
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_boot.h>
+
+void
+ao_boot_chain(uint32_t *base)
+{
+       uint32_t        sp;
+       uint32_t        pc;
+
+       sp = base[0];
+       pc = base[1];
+       if (0x08000100 <= pc && pc <= 0x08200000 && (pc & 1) == 1) {
+               asm ("mov sp, %0" : : "r" (sp));
+               asm ("mov lr, %0" : : "r" (pc));
+               asm ("bx lr");
+       }
+}
+
+#define AO_BOOT_SIGNAL 0x5a5aa5a5
+#define AO_BOOT_CHECK  0xc3c33c3c
+
+struct ao_boot {
+       uint32_t        *base;
+       uint32_t        signal;
+       uint32_t        check;
+};
+
+struct ao_boot ao_boot __attribute__((section(".preserve.2")));
+
+int
+ao_boot_check_chain(void)
+{
+       if (ao_boot.signal == AO_BOOT_SIGNAL && ao_boot.check == AO_BOOT_CHECK) {
+               ao_boot.signal = 0;
+               ao_boot.check = 0;
+               if (ao_boot.base == AO_BOOT_FORCE_LOADER)
+                       return 0;
+               ao_boot_chain(ao_boot.base);
+       }
+       return 1;
+}
+
+void
+ao_boot_reboot(uint32_t *base)
+{
+       ao_boot.base = base;
+       ao_boot.signal = AO_BOOT_SIGNAL;
+       ao_boot.check = AO_BOOT_CHECK;
+       ao_arch_reboot();
+}
diff --git a/src/stm32f1/ao_boot_pin.c b/src/stm32f1/ao_boot_pin.c
new file mode 100644 (file)
index 0000000..e1c21c8
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_boot.h>
+#include <ao_exti.h>
+
+int
+ao_boot_check_pin(void)
+{
+       uint16_t v;
+
+       /* Enable power interface clock */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
+
+       /* Enable the input pin */
+       ao_enable_input(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN,
+                       AO_BOOT_APPLICATION_MODE);
+
+       for (v = 0; v < 100; v++)
+               ao_arch_nop();
+
+       /* Read the value */
+       v = ao_gpio_get(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN);
+
+       /* Reset the chip to turn off the port and the power interface clock */
+       ao_gpio_set_mode(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN, 0);
+       ao_disable_port(&AO_BOOT_APPLICATION_GPIO);
+
+       stm_rcc.apb1enr &= ~(1UL << STM_RCC_APB1ENR_PWREN);
+       return v == AO_BOOT_APPLICATION_VALUE;
+}
diff --git a/src/stm32f1/ao_clock.c b/src/stm32f1/ao_clock.c
new file mode 100644 (file)
index 0000000..899923d
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Copyright © 2023 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+void
+ao_clock_init(void)
+{
+       uint32_t        cfgr;
+
+       /* Switch to HSI while messing about */
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSION);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
+               ao_arch_nop();
+
+       stm_rcc.cfgr = (stm_rcc.cfgr & ~(uint32_t) (STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW)) |
+               (STM_RCC_CFGR_SW_HSI << STM_RCC_CFGR_SW);
+
+       /* wait for system to switch to HSI */
+       while ((stm_rcc.cfgr & (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS)) !=
+              (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS))
+               ao_arch_nop();
+
+       /* Disable all interrupts */
+       stm_rcc.cir = 0;
+
+#if AO_HSE
+#if AO_HSE_BYPASS
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSEBYP);
+#else
+       stm_rcc.cr &= ~(uint32_t) (1 << STM_RCC_CR_HSEBYP);
+#endif
+       /* Enable HSE clock */
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSEON);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSERDY)))
+               asm("nop");
+
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK          (STM_RCC_CFGR_SWS_HSE << STM_RCC_CFGR_SWS)
+#define STM_RCC_CFGR_SW_TARGET_CLOCK           (STM_RCC_CFGR_SW_HSE)
+#define STM_PLLSRC                             AO_HSE
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK       (STM_RCC_CFGR_PLLSRC_HSE << STM_RCC_CFGR_PLLSRC)
+#else
+#define STM_HSI                                16000000
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK          (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS)
+#define STM_RCC_CFGR_SW_TARGET_CLOCK           (STM_RCC_CFGR_SW_HSI)
+#define STM_PLLSRC                             (STM_HSI/2)
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK       (STM_RCC_CFGR_PLLSRC_HSI_2 << STM_RCC_CFGR_PLLSRC)
+#endif
+
+#if !AO_HSE || HAS_ADC || HAS_ADC_SINGLE
+       /* Enable HSI RC clock 16MHz */
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSION);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
+               asm("nop");
+#endif
+
+       /* Set flash latency to tolerate 72MHz SYSCLK  -> 2 wait states */
+
+       /* Enable 64-bit access and prefetch */
+       stm_flash.acr = ((1 << STM_FLASH_ACR_PRFTBE) |
+                        (0 << STM_FLASH_ACR_HLFCYA) |
+                        (STM_FLASH_ACR_LATENCY_2 << STM_FLASH_ACR_LATENCY));
+
+       /* Enable power interface clock */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
+
+#if 0
+       /* Set voltage range to 1.8V */
+
+       /* poll VOSF bit in PWR_CSR. Wait until it is reset to 0 */
+       while ((stm_pwr.csr & (1 << STM_PWR_CSR_VOSF)) != 0)
+               asm("nop");
+
+       /* Configure voltage scaling range */
+       cr = stm_pwr.cr;
+       cr &= ~(STM_PWR_CR_VOS_MASK << STM_PWR_CR_VOS);
+       cr |= (STM_PWR_CR_VOS_1_8 << STM_PWR_CR_VOS);
+       stm_pwr.cr = cr;
+
+       /* poll VOSF bit in PWR_CSR. Wait until it is reset to 0 */
+       while ((stm_pwr.csr & (1 << STM_PWR_CSR_VOSF)) != 0)
+               asm("nop");
+#endif
+
+       /* HCLK */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE);
+       cfgr |= (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE);
+       stm_rcc.cfgr = cfgr;
+       while ((stm_rcc.cfgr & (STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE)) !=
+              (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE))
+               asm ("nop");
+
+       /* APB1 Prescaler = AO_APB1_PRESCALER */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PPRE1_MASK << STM_RCC_CFGR_PPRE1);
+       cfgr |= (AO_RCC_CFGR_PPRE1_DIV << STM_RCC_CFGR_PPRE1);
+       stm_rcc.cfgr = cfgr;
+
+       /* APB2 Prescaler = AO_APB2_PRESCALER */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PPRE2_MASK << STM_RCC_CFGR_PPRE2);
+       cfgr |= (AO_RCC_CFGR_PPRE2_DIV << STM_RCC_CFGR_PPRE2);
+       stm_rcc.cfgr = cfgr;
+
+       /* ADC Prescaler */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_ADCPRE_MASK << STM_RCC_CFGR_ADCPRE);
+       cfgr |= (AO_RCC_CFGR_ADCPRE << STM_RCC_CFGR_ADCPRE);
+       stm_rcc.cfgr = cfgr;
+
+       /* Disable the PLL */
+       stm_rcc.cr &= ~(1UL << STM_RCC_CR_PLLON);
+       while (stm_rcc.cr & (1UL << STM_RCC_CR_PLLRDY))
+               asm("nop");
+
+       /* PLLMUL */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PLLMUL_MASK << STM_RCC_CFGR_PLLMUL);
+       cfgr |= (AO_RCC_CFGR_PLLMUL << STM_RCC_CFGR_PLLMUL);
+
+       /* PLLXTPRE */
+       cfgr &= ~(STM_RCC_CFGR_PLLXTPRE_MASK << STM_RCC_CFGR_PLLXTPRE);
+       cfgr |= (AO_RCC_CFGR_PLLXTPRE << STM_RCC_CFGR_PLLXTPRE);
+
+       /* PLL source */
+       cfgr &= ~(1UL << STM_RCC_CFGR_PLLSRC);
+       cfgr |= STM_RCC_CFGR_PLLSRC_TARGET_CLOCK;
+
+       stm_rcc.cfgr = cfgr;
+
+       /* Enable the PLL and wait for it */
+       stm_rcc.cr |= (1 << STM_RCC_CR_PLLON);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY)))
+               asm("nop");
+
+       /* Switch to the PLL for the system clock */
+
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW);
+       cfgr |= (STM_RCC_CFGR_SW_PLL << STM_RCC_CFGR_SW);
+       stm_rcc.cfgr = cfgr;
+       for (;;) {
+               uint32_t        c, part, mask, val;
+
+               c = stm_rcc.cfgr;
+               mask = (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS);
+               val = (STM_RCC_CFGR_SWS_PLL << STM_RCC_CFGR_SWS);
+               part = c & mask;
+               if (part == val)
+                       break;
+       }
+
+#if 0
+       stm_rcc.apb2rstr = 0xffff;
+       stm_rcc.apb1rstr = 0xffff;
+       stm_rcc.ahbrstr = 0x3f;
+       stm_rcc.ahbenr = (1 << STM_RCC_AHBENR_FLITFEN);
+       stm_rcc.apb2enr = 0;
+       stm_rcc.apb1enr = 0;
+       stm_rcc.ahbrstr = 0;
+       stm_rcc.apb1rstr = 0;
+       stm_rcc.apb2rstr = 0;
+#endif
+
+       /* Clear reset flags */
+       stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
+
+       /* Enable AFIO */
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_AFIOEN);
+
+       /* Release PB3, PA15 and PB4 from JTAG use */
+       stm_afio.mapr = (stm_afio.mapr &
+                        ~(STM_AFIO_MAPR_SWJ_CFG_MASK << STM_AFIO_MAPR_SWJ_CFG)) |
+               STM_AFIO_MAPR_SWJ_CFG_SW_DP << STM_AFIO_MAPR_SWJ_CFG;
+
+#if DEBUG_THE_CLOCK
+       /* Output SYSCLK on PA8 for measurments */
+
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+
+       stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0);
+       stm_moder_set(&stm_gpioa, 8, STM_MODER_ALTERNATE);
+       stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_40MHz);
+
+       stm_rcc.cfgr |= (STM_RCC_CFGR_MCOPRE_DIV_1 << STM_RCC_CFGR_MCOPRE);
+       stm_rcc.cfgr |= (STM_RCC_CFGR_MCOSEL_HSE << STM_RCC_CFGR_MCOSEL);
+#endif
+}
diff --git a/src/stm32f1/ao_dma_stm.c b/src/stm32f1/ao_dma_stm.c
new file mode 100644 (file)
index 0000000..45bb88f
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+#define NUM_DMA        7
+
+struct ao_dma_config {
+       void            (*isr)(int index);
+};
+
+uint8_t ao_dma_done[NUM_DMA];
+
+static struct ao_dma_config ao_dma_config[NUM_DMA];
+static uint8_t ao_dma_allocated[NUM_DMA];
+static uint8_t ao_dma_mutex[NUM_DMA];
+
+static void
+ao_dma_isr(uint8_t index) {
+       /* Get channel interrupt bits */
+       uint32_t        isr = stm_dma.isr & (STM_DMA_ISR_MASK <<
+                                            STM_DMA_ISR(index));
+
+       /* Ack them */
+       stm_dma.ifcr = isr;
+       if (ao_dma_config[index].isr)
+               (*ao_dma_config[index].isr)(index);
+       else {
+               ao_dma_done[index] = 1;
+               ao_wakeup(&ao_dma_done[index]);
+       }
+}
+
+void stm_dma1_channel1_isr(void) { ao_dma_isr(STM_DMA_INDEX(1)); }
+void stm_dma1_channel2_isr(void) { ao_dma_isr(STM_DMA_INDEX(2)); }
+#ifdef STM_DMA1_3_STOLEN
+#define LEAVE_DMA_ON
+#else
+void stm_dma1_channel3_isr(void) { ao_dma_isr(STM_DMA_INDEX(3)); }
+#endif
+void stm_dma1_channel4_isr(void) { ao_dma_isr(STM_DMA_INDEX(4)); }
+#ifdef STM_DMA1_5_STOLEN
+#define LEAVE_DMA_ON
+#else
+void stm_dma1_channel5_isr(void) { ao_dma_isr(STM_DMA_INDEX(5)); }
+#endif
+void stm_dma1_channel6_isr(void) { ao_dma_isr(STM_DMA_INDEX(6)); }
+void stm_dma1_channel7_isr(void) { ao_dma_isr(STM_DMA_INDEX(7)); }
+
+#ifndef LEAVE_DMA_ON
+static uint8_t ao_dma_active;
+#endif
+
+void
+ao_dma_mutex_get(uint8_t index)
+{
+       if (ao_dma_allocated[index]) {
+               if (ao_dma_mutex[index])
+                       ao_panic(AO_PANIC_DMA);
+               ao_dma_mutex[index] = 0xff;
+       } else
+               ao_mutex_get(&ao_dma_mutex[index]);
+}
+
+void
+ao_dma_mutex_put(uint8_t index)
+{
+       if (ao_dma_allocated[index])
+               ao_dma_mutex[index] = 0;
+       else
+               ao_mutex_put(&ao_dma_mutex[index]);
+}
+
+
+void
+ao_dma_set_transfer(uint8_t            index,
+                   volatile void       *peripheral,
+                   void                *memory,
+                   uint16_t            count,
+                   uint32_t            ccr)
+{
+       if (ao_dma_allocated[index]) {
+               if (ao_dma_mutex[index])
+                       ao_panic(AO_PANIC_DMA);
+               ao_dma_mutex[index] = 0xff;
+       } else
+               ao_mutex_get(&ao_dma_mutex[index]);
+#ifndef LEAVE_DMA_ON
+       ao_arch_critical(
+               if (ao_dma_active++ == 0)
+                       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+               );
+#endif
+       stm_dma.channel[index].ccr = ccr | (1 << STM_DMA_CCR_TCIE);
+       stm_dma.channel[index].cndtr = count;
+       stm_dma.channel[index].cpar = peripheral;
+       stm_dma.channel[index].cmar = memory;
+       ao_dma_config[index].isr = NULL;
+}
+
+void
+ao_dma_set_isr(uint8_t index, void (*isr)(int))
+{
+       ao_dma_config[index].isr = isr;
+}
+
+void
+ao_dma_start(uint8_t index)
+{
+       ao_dma_done[index] = 0;
+       stm_dma.channel[index].ccr |= (1UL << STM_DMA_CCR_EN);
+}
+
+void
+ao_dma_done_transfer(uint8_t index)
+{
+       stm_dma.channel[index].ccr &= ~(1UL << STM_DMA_CCR_EN);
+#ifndef LEAVE_DMA_ON
+       ao_arch_critical(
+               if (--ao_dma_active == 0)
+                       stm_rcc.ahbenr &= ~(1UL << STM_RCC_AHBENR_DMA1EN);
+               );
+#endif
+       if (ao_dma_allocated[index])
+               ao_dma_mutex[index] = 0;
+       else
+               ao_mutex_put(&ao_dma_mutex[index]);
+}
+
+void
+ao_dma_alloc(uint8_t index)
+{
+       if (ao_dma_allocated[index])
+               ao_panic(AO_PANIC_DMA);
+       ao_dma_allocated[index] = 1;
+}
+
+#if DEBUG
+void
+ao_dma_dump_cmd(void)
+{
+       int i;
+
+#ifndef LEAVE_DMA_ON
+       ao_arch_critical(
+               if (ao_dma_active++ == 0)
+                       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+               );
+#endif
+       printf ("isr %08x ifcr%08x\n", stm_dma.isr, stm_dma.ifcr);
+       for (i = 0; i < NUM_DMA; i++)
+               printf("%d: done %d allocated %d mutex %2d ccr %04x cndtr %04x cpar %08x cmar %08x isr %08x\n",
+                      i,
+                      ao_dma_done[i],
+                      ao_dma_allocated[i],
+                      ao_dma_mutex[i],
+                      stm_dma.channel[i].ccr,
+                      stm_dma.channel[i].cndtr,
+                      stm_dma.channel[i].cpar,
+                      stm_dma.channel[i].cmar,
+                      ao_dma_config[i].isr);
+#ifndef LEAVE_DMA_ON
+       ao_arch_critical(
+               if (--ao_dma_active == 0)
+                       stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_DMA1EN);
+               );
+#endif
+}
+
+static const struct ao_cmds ao_dma_cmds[] = {
+       { ao_dma_dump_cmd,      "D\0Dump DMA status" },
+       { 0, NULL }
+};
+#endif
+
+void
+ao_dma_init(void)
+{
+       int     index;
+
+#ifdef LEAVE_DMA_ON
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+#endif
+       for (index = 0; index < STM_NUM_DMA; index++) {
+#if STM_DMA1_5_STOLEN
+               if (index == STM_DMA_INDEX(5)) {
+                       ao_dma_allocated[index] = 1;
+                       ao_dma_mutex[index] = 0xff;
+                       continue;
+               }
+#endif
+#if STM_DMA1_3_STOLEN
+               if (index == STM_DMA_INDEX(3)) {
+                       ao_dma_allocated[index] = 1;
+                       ao_dma_mutex[index] = 0xff;
+                       continue;
+               }
+#endif
+               stm_nvic_set_enable(STM_ISR_DMA1_CHANNEL1_POS + index);
+               stm_nvic_set_priority(STM_ISR_DMA1_CHANNEL1_POS + index,
+                                     AO_STM_NVIC_MED_PRIORITY);
+               ao_dma_allocated[index] = 0;
+               ao_dma_mutex[index] = 0;
+       }
+#if DEBUG
+       ao_cmd_register(&ao_dma_cmds[0]);
+#endif
+}
diff --git a/src/stm32f1/ao_eeprom.c b/src/stm32f1/ao_eeprom.c
new file mode 100644 (file)
index 0000000..ec6654d
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2024 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_flash.h>
+
+extern uint8_t __eeprom_base[2048];
+
+uint8_t
+ao_eeprom_read(ao_pos_t pos, void *v, uint16_t len)
+{
+       memcpy(v, &__eeprom_base[pos], len);
+       return 1;
+}
+
+uint8_t
+ao_eeprom_write(ao_pos_t pos, void *v, uint16_t len)
+{
+       bool hsi_on = (stm_rcc.cr & (1UL << STM_RCC_CR_HSIRDY)) != 0;
+
+       if (!hsi_on) {
+               stm_rcc.cr |= (1UL << STM_RCC_CR_HSION);
+               while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
+                       ao_arch_nop();
+
+       }
+       ao_flash_bytes(&__eeprom_base[pos], v, len);
+
+       if (!hsi_on) {
+               stm_rcc.cr &= ~(1UL << STM_RCC_CR_HSION);
+       }
+
+       return 1;
+}
+
diff --git a/src/stm32f1/ao_exti.h b/src/stm32f1/ao_exti.h
new file mode 100644 (file)
index 0000000..d072d14
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_EXTI_H_
+#define _AO_EXTI_H_
+
+void
+ao_exti_setup(struct stm_gpio *gpio, uint8_t pin, uint8_t mode, void (*callback)(void));
+
+void
+ao_exti_set_mode(struct stm_gpio *gpio, uint8_t pin, uint8_t mode);
+
+void
+ao_exti_set_callback(struct stm_gpio *gpio, uint8_t pin, void (*callback)(void));
+
+void
+ao_exti_enable(struct stm_gpio *gpio, uint8_t pin);
+
+void
+ao_exti_disable(struct stm_gpio *gpio, uint8_t pin);
+
+void
+ao_exti_init(void);
+
+#endif /* _AO_EXTI_H_ */
diff --git a/src/stm32f1/ao_exti_stm.c b/src/stm32f1/ao_exti_stm.c
new file mode 100644 (file)
index 0000000..b2157ef
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+
+static void    (*ao_exti_callback[16])(void);
+
+uint32_t       ao_last_exti;
+
+static void ao_exti_one_isr(uint8_t pin) {
+       uint32_t        pending = (ao_last_exti = stm_exti.pr) & (1 << pin);
+
+       stm_exti.pr = pending;
+       if (pending && ao_exti_callback[pin])
+               (*ao_exti_callback[pin])();
+}
+
+static void ao_exti_range_isr(uint8_t first, uint8_t last, uint16_t mask) {
+       uint16_t        pending = (uint16_t) (ao_last_exti = stm_exti.pr) & mask;
+       uint8_t         pin;
+       static uint16_t last_mask;
+       static uint8_t  last_pin;
+
+       if (pending == last_mask) {
+               stm_exti.pr = last_mask;
+               (*ao_exti_callback[last_pin])();
+               return;
+       }
+       stm_exti.pr = pending;
+       for (pin = first; pin <= last; pin++)
+               if ((pending & ((uint32_t) 1 << pin)) && ao_exti_callback[pin]) {
+                       last_mask = (1 << pin);
+                       last_pin = pin;
+                       (*ao_exti_callback[pin])();
+               }
+}
+
+void stm_exti0_isr(void) { ao_exti_one_isr(0); }
+void stm_exti1_isr(void) { ao_exti_one_isr(1); }
+void stm_exti2_isr(void) { ao_exti_one_isr(2); }
+void stm_exti3_isr(void) { ao_exti_one_isr(3); }
+void stm_exti4_isr(void) { ao_exti_one_isr(4); }
+void stm_exti9_5_isr(void) { ao_exti_range_isr(5, 9, 0x3e0); }
+void stm_exti15_10_isr(void) { ao_exti_range_isr(10, 15, 0xfc00); }
+
+void
+ao_exti_setup (struct stm_gpio *gpio, uint8_t pin, uint8_t mode, void (*callback)(void)) {
+       uint32_t        mask = 1 << pin;
+       uint8_t         irq;
+       uint8_t         prio;
+
+       ao_exti_callback[pin] = callback;
+
+       /* configure gpio to interrupt routing */
+       stm_exticr_set(gpio, pin);
+
+       if (!(mode & AO_EXTI_PIN_NOCONFIGURE)) {
+               /* configure pin as input, setting selected pull-up/down mode */
+               ao_enable_input(gpio, pin, mode);
+       }
+
+       /* Set interrupt mask and rising/falling mode */
+       stm_exti.imr &= ~mask;
+       if (mode & AO_EXTI_MODE_RISING)
+               stm_exti.rtsr |= mask;
+       else
+               stm_exti.rtsr &= ~mask;
+       if (mode & AO_EXTI_MODE_FALLING)
+               stm_exti.ftsr |= mask;
+       else
+               stm_exti.ftsr &= ~mask;
+
+       if (pin <= 4)
+               irq = STM_ISR_EXTI0_POS + pin;
+       else if (pin <= 9)
+               irq = STM_ISR_EXTI9_5_POS;
+       else
+               irq = STM_ISR_EXTI15_10_POS;
+
+       /* Set priority */
+       prio = AO_STM_NVIC_MED_PRIORITY;
+       if (mode & AO_EXTI_PRIORITY_LOW)
+               prio = AO_STM_NVIC_LOW_PRIORITY;
+       else if (mode & AO_EXTI_PRIORITY_HIGH)
+               prio = AO_STM_NVIC_HIGH_PRIORITY;
+
+       stm_nvic_set_priority(irq, prio);
+       stm_nvic_set_enable(irq);
+}
+
+void
+ao_exti_set_mode(struct stm_gpio *gpio, uint8_t pin, uint8_t mode) {
+       (void) gpio;
+
+       uint32_t        mask = 1 << pin;
+
+       if (mode & AO_EXTI_MODE_RISING)
+               stm_exti.rtsr |= mask;
+       else
+               stm_exti.rtsr &= ~mask;
+       if (mode & AO_EXTI_MODE_FALLING)
+               stm_exti.ftsr |= mask;
+       else
+               stm_exti.ftsr &= ~mask;
+}
+
+void
+ao_exti_set_callback(struct stm_gpio *gpio, uint8_t pin, void (*callback)(void)) {
+       (void) gpio;
+       ao_exti_callback[pin] = callback;
+}
+
+void
+ao_exti_enable(struct stm_gpio *gpio, uint8_t pin) {
+       uint32_t        mask = (1 << pin);
+       (void) gpio;
+       stm_exti.pr = mask;
+       stm_exti.imr |= mask;
+}
+
+void
+ao_exti_disable(struct stm_gpio *gpio, uint8_t pin) {
+       uint32_t        mask = (1 << pin);
+       (void) gpio;
+       stm_exti.imr &= ~mask;
+       stm_exti.pr = mask;
+}
+
+void
+ao_exti_init(void)
+{
+}
diff --git a/src/stm32f1/ao_fast_timer.c b/src/stm32f1/ao_fast_timer.c
new file mode 100644 (file)
index 0000000..86e74c1
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright © 2024 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include <ao.h>
+#include <ao_fast_timer.h>
+
+static void (*ao_fast_timer_callback[AO_FAST_TIMER_MAX])(void);
+static uint8_t ao_fast_timer_count;
+static uint8_t ao_fast_timer_users;
+
+#if AO_FAST_TIMER == 1
+
+# define AO_FAST_TIMER_TYPE 18
+# define stm_tim               stm_tim1
+# define stm_tim_isr           stm_tim1_up_isr
+# define STM_ISR_TIM_POS       STM_ISR_TIM1_UP_POS
+# define STM_RCC_APBENR_TIMEN  STM_RCC_APB2ENR_TIM1EN
+
+#elif AO_FAST_TIMER == 2
+
+# define AO_FAST_TIMER_TYPE 234
+# define stm_tim               stm_tim2
+# define stm_tim_isr           stm_tim2_isr
+# define STM_ISR_TIM_POS       STM_ISR_TIM2_POS
+# define STM_RCC_APBENR_TIMEN  STM_RCC_APB1ENR_TIM2EN
+
+#elif AO_FAST_TIMER == 3
+
+# define AO_FAST_TIMER_TYPE 234
+# define stm_tim               stm_tim3
+# define stm_tim_isr           stm_tim3_isr
+# define STM_ISR_TIM_POS       STM_ISR_TIM3_POS
+# define STM_RCC_APBENR_TIMEN  STM_RCC_APB1ENR_TIM3EN
+
+#elif AO_FAST_TIMER == 4
+
+# define AO_FAST_TIMER_TYPE 234
+# define stm_tim               stm_tim4
+# define stm_tim_isr           stm_tim4_isr
+# define STM_ISR_TIM_POS       STM_ISR_TIM4_POS
+# define STM_RCC_APBENR_TIMEN  STM_RCC_APB1ENR_TIM4EN
+
+#else
+#error AO_FAST_TIMER
+#endif
+
+#if AO_FAST_TIMER_TYPE == 18
+
+#define STM_TIM_CR1(cen) ((0 << STM_TIM18_CR1_CKD) |   \
+                         (0 << STM_TIM18_CR1_ARPE) |   \
+                         (0 << STM_TIM18_CR1_CMS) |    \
+                         (0 << STM_TIM18_CR1_DIR) |    \
+                         (0 << STM_TIM18_CR1_OPM) |    \
+                         (1 << STM_TIM18_CR1_URS) |    \
+                         (0 << STM_TIM18_CR1_UDIS) |   \
+                         ((cen) << STM_TIM18_CR1_CEN))
+#define STM_TIM_SR_UIF         STM_TIM18_SR_UIF
+#define STM_TIM_DIER_UIE       STM_TIM18_DIER_UIE
+#define STM_TIM_EGR_UG         STM_TIM18_EGR_UG
+#define STM_TIM_CR2_MMS                STM_TIM18_CR2_MMS
+#define STM_TIM_CR2_MMS_RESET  STM_TIM18_CR2_MMS_RESET
+
+#define AO_TIM_PCLK    AO_PCLK2
+
+/*
+ * According to the STM clock-configuration, timers 18 run
+ * twice as fast as the APB2 clock *if* the APB2 prescaler
+ * is greater than 1.
+ */
+
+#if AO_APB2_PRESCALER > 1
+#define AO_TIM_SCALER 2
+#else
+#define AO_TIM_SCALER 1
+#endif
+
+#define STM_RCC_APB_TIM        stm_rcc.apb2enr
+
+#elif AO_FAST_TIMER_TYPE == 234
+
+#define STM_TIM_CR1(cen) ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) | \
+                         (0 << STM_TIM234_CR1_ARPE) |                  \
+                         (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) | \
+                         (0 << STM_TIM234_CR1_DIR) |                   \
+                         (0 << STM_TIM234_CR1_OPM) |                   \
+                         (0 << STM_TIM234_CR1_URS) |                   \
+                         (0 << STM_TIM234_CR1_UDIS) |                  \
+                         ((cen) << STM_TIM234_CR1_CEN))                \
+
+#define AO_TIM_PCLK    AO_PCLK1
+
+/*
+ * According to the STM clock-configuration, timers 234 run
+ * twice as fast as the APB1 clock *if* the APB1 prescaler
+ * is greater than 1.
+ */
+
+#if AO_APB1_PRESCALER > 1
+#define AO_TIM_SCALER 2
+#else
+#define AO_TIM_SCALER 1
+#endif
+
+#define STM_TIM_SR_UIF         STM_TIM234_SR_UIF
+#define STM_TIM_DIER_UIE       STM_TIM234_DIER_UIE
+#define STM_TIM_EGR_UG         STM_TIM234_EGR_UG
+#define STM_TIM_CR2_MMS                STM_TIM234_CR2_MMS
+#define STM_TIM_CR2_MMS_RESET  STM_TIM234_CR2_MMS_RESET
+
+#define STM_RCC_APB_TIM        stm_rcc.apb1enr
+
+#endif
+
+static void
+ao_fast_timer_enable(void)
+{
+       stm_tim.cr1 = STM_TIM_CR1(1);
+}
+
+static void
+ao_fast_timer_disable(void)
+{
+       stm_tim.cr1 = STM_TIM_CR1(0);
+}
+
+void
+ao_fast_timer_on(void (*callback)(void))
+{
+       ao_fast_timer_callback[ao_fast_timer_count] = callback;
+       if (!ao_fast_timer_count++)
+               ao_fast_timer_enable();
+}
+
+void
+ao_fast_timer_off(void (*callback)(void))
+{
+       uint8_t n;
+
+       for (n = 0; n < ao_fast_timer_count; n++)
+               if (ao_fast_timer_callback[n] == callback) {
+                       for (; n < ao_fast_timer_count-1; n++) {
+                               ao_fast_timer_callback[n] = ao_fast_timer_callback[n+1];
+                       }
+                       if (!--ao_fast_timer_count)
+                               ao_fast_timer_disable();
+                       break;
+               }
+}
+
+void stm_tim_isr(void)
+{
+       uint8_t i;
+       if (stm_tim.sr & (1 << STM_TIM_SR_UIF)) {
+               stm_tim.sr = 0;
+
+               for (i = 0; i < ao_fast_timer_count; i++)
+                       (*ao_fast_timer_callback[i])();
+       }
+}
+
+#ifndef FAST_TIMER_FREQ
+#define FAST_TIMER_FREQ        10000
+#endif
+
+#define TIMER_FAST     ((AO_TIM_PCLK * AO_TIM_SCALER) / FAST_TIMER_FREQ)
+
+void
+ao_fast_timer_init(void)
+{
+       if (!ao_fast_timer_users) {
+               stm_nvic_set_enable(STM_ISR_TIM_POS);
+               stm_nvic_set_priority(STM_ISR_TIM_POS, AO_STM_NVIC_CLOCK_PRIORITY);
+
+               /* Turn on timer 1 */
+               STM_RCC_APB_TIM |= (1 << STM_RCC_APBENR_TIMEN);
+
+               stm_tim.psc = TIMER_FAST;
+               stm_tim.arr = 9;
+               stm_tim.cnt = 0;
+
+               /* Enable update interrupt */
+               stm_tim.dier = (1 << STM_TIM_DIER_UIE);
+
+               /* Poke timer to reload values */
+               stm_tim.egr |= (1 << STM_TIM_EGR_UG);
+
+               stm_tim.cr2 = (STM_TIM_CR2_MMS_RESET << STM_TIM_CR2_MMS);
+               ao_fast_timer_disable();
+       }
+       if (ao_fast_timer_users == AO_FAST_TIMER_MAX)
+               ao_panic(AO_PANIC_FAST_TIMER);
+       ao_fast_timer_users++;
+}
+
diff --git a/src/stm32f1/ao_flash.h b/src/stm32f1/ao_flash.h
new file mode 100644 (file)
index 0000000..9937a51
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_FLASH_STM_H_
+#define _AO_FLASH_STM_H_
+
+void
+ao_flash_erase_page(uint32_t *page);
+
+void
+ao_flash_page(uint32_t *page, uint32_t *src);
+
+void
+ao_flash_bytes(void *page, void *src, size_t size);
+
+#endif /* _AO_FLASH_STM_H_ */
diff --git a/src/stm32f1/ao_flash_loader_stm.c b/src/stm32f1/ao_flash_loader_stm.c
new file mode 100644 (file)
index 0000000..18bf272
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_exti.h>
+#include <ao_boot.h>
+#include <ao_flash_task.h>
+
+int
+main(void)
+{
+       ao_clock_init();
+
+       ao_usb_init();
+
+#if HAS_TICK
+       ao_timer_init();
+#endif
+
+#ifdef AO_FLASH_LOADER_INIT
+       AO_FLASH_LOADER_INIT;
+#endif
+       ao_flash_task();
+       return 0;
+}
diff --git a/src/stm32f1/ao_flash_stm.c b/src/stm32f1/ao_flash_stm.c
new file mode 100644 (file)
index 0000000..1e35545
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright © 2023 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_flash.h>
+
+/* Note that the HSI clock must be running for this code to work.
+ * Also, special care must be taken with the linker to ensure that the
+ * functions marked 'ramtext' land in ram and not rom. An example of that
+ * can be found in altos-loader.ld
+ */
+
+static uint8_t
+ao_flash_is_locked(void)
+{
+       return (stm_flash.cr & (1 << STM_FLASH_CR_LOCK)) != 0;
+}
+
+static void
+ao_flash_unlock(void)
+{
+       if (!ao_flash_is_locked())
+               return;
+
+       /* Unlock FLASH_CR register */
+       stm_flash.keyr = STM_FLASH_KEYR_KEY1;
+       stm_flash.keyr = STM_FLASH_KEYR_KEY2;
+       if (ao_flash_is_locked())
+               ao_panic(AO_PANIC_FLASH);
+}
+
+static void
+ao_flash_lock(void)
+{
+       stm_flash.cr |= (1 << STM_FLASH_CR_LOCK);
+}
+
+#define ao_flash_wait_bsy() do { while (stm_flash.sr & (1 << STM_FLASH_SR_BSY)); } while (0)
+
+static void __attribute__ ((section(".sdata2.flash"), noinline))
+_ao_flash_erase_page(uint32_t *page)
+{
+       stm_flash.cr |= (1 << STM_FLASH_CR_PER);
+
+       stm_flash.ar = (uintptr_t) page;
+
+       stm_flash.cr |= (1 << STM_FLASH_CR_STRT);
+
+       ao_flash_wait_bsy();
+
+       stm_flash.cr &= ~(1UL << STM_FLASH_CR_PER);
+}
+
+static uint32_t
+stm_flash_page_size(void)
+{
+       uint16_t        f_size = stm_flash_data.f_size;
+
+       if (f_size <= 128) {
+               /* low-density and medium-density devices */
+               return 1024;
+       } else {
+               /*
+                * high-density devices, XL-density devices and
+                * Connectivity devices
+                */
+               return 2048;
+       }
+}
+
+void
+ao_flash_erase_page(uint32_t *page)
+{
+       /* Erase the whole page at the start. This assumes we'll be flashing things
+        * in memory order
+        */
+
+       if ((uintptr_t) page & (stm_flash_page_size() - 1))
+               return;
+
+       ao_arch_block_interrupts();
+       ao_flash_unlock();
+
+       _ao_flash_erase_page(page);
+
+       ao_flash_lock();
+       ao_arch_release_interrupts();
+}
+
+static void __attribute__ ((section(".sdata2.flash"), noinline))
+_ao_flash_page(uint16_t *dst, uint16_t *src, unsigned int shorts)
+{
+       uint8_t         i;
+
+       stm_flash.cr |= (1 << STM_FLASH_CR_PG);
+
+       for (i = 0; i < shorts; i++) {
+               *dst++ = *src++;
+               ao_flash_wait_bsy();
+       }
+
+       stm_flash.cr &= ~(1UL << STM_FLASH_CR_PG);
+}
+
+void
+ao_flash_page(uint32_t *page, uint32_t *src)
+{
+       ao_flash_erase_page(page);
+
+       ao_arch_block_interrupts();
+       ao_flash_unlock();
+
+       _ao_flash_page((uint16_t *) page, (uint16_t *) src, 128);
+
+       ao_flash_lock();
+       ao_arch_release_interrupts();
+}
+
+/* Stores less than a full page while still smashing the full page */
+void
+ao_flash_bytes(void *page, void *src, size_t size)
+{
+       unsigned int shorts = (unsigned int) ((size + 1) >> 1);
+
+       ao_flash_erase_page(page);
+
+       ao_arch_block_interrupts();
+       ao_flash_unlock();
+
+       _ao_flash_page((uint16_t *) page, (uint16_t *) src, shorts);
+
+       ao_flash_lock();
+       ao_arch_release_interrupts();
+}
diff --git a/src/stm32f1/ao_flash_stm_pins.h b/src/stm32f1/ao_flash_stm_pins.h
new file mode 100644 (file)
index 0000000..b58bd36
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_FLASH_STM_PINS_H_
+#define _AO_FLASH_STM_PINS_H_
+
+#include <ao_flash_pins.h>
+
+#ifndef AO_SYSCLK
+
+#define AO_SYSCLK      72000000
+#define AO_HCLK                72000000
+#define AO_APB1CLK     36000000
+#define AO_APB2CLK     72000000
+#define AO_ADCCLK      12000000
+
+#define AO_RCC_CFGR_USBPRE     STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL     STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE   STM_RCC_CFGR_PLLXTPRE_1
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE1_DIV_2
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+#define AO_RCC_CFGR_ADCPRE     STM_RCC_CFGR_ADCPRE_6
+
+#endif
+
+#endif /* _AO_FLASH_STM_PINS_H_ */
diff --git a/src/stm32f1/ao_i2c_stm.c b/src/stm32f1/ao_i2c_stm.c
new file mode 100644 (file)
index 0000000..a61d3eb
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+
+struct ao_i2c_stm_info {
+       uint8_t tx_dma_index;
+       uint8_t rx_dma_index;
+       struct stm_i2c  *stm_i2c;
+};
+
+#define I2C_FAST       1
+
+#define I2C_TIMEOUT    100
+
+#define I2C_IDLE       0
+#define I2C_RUNNING    1
+#define I2C_ERROR      2
+
+static uint8_t ao_i2c_state[STM_NUM_I2C];
+static uint16_t        ao_i2c_addr[STM_NUM_I2C];
+uint8_t        ao_i2c_mutex[STM_NUM_I2C];
+
+# define I2C_HIGH_SLOW 5000    /* ns, 100kHz clock */
+#ifdef TELEMEGA
+# define I2C_HIGH_FAST 2000    /* ns, 167kHz clock */
+#else
+# define I2C_HIGH_FAST 1000    /* ns, 333kHz clock */
+#endif
+
+# define I2C_RISE_SLOW 500     /* ns */
+# define I2C_RISE_FAST 100     /* ns */
+
+/* Clock period in ns */
+#define CYCLES(period) (((period) * (AO_PCLK1 / 1000)) / 1000000)
+
+#define max(a,b)       ((a) > (b) ? (a) : (b))
+#define I2C_CCR_HIGH_SLOW      max(4,CYCLES(I2C_HIGH_SLOW))
+#define I2C_CCR_HIGH_FAST      max(4,CYCLES(I2C_HIGH_FAST))
+#define I2C_TRISE_SLOW         (CYCLES(I2C_RISE_SLOW) + 1)
+#define I2C_TRISE_FAST         (CYCLES(I2C_RISE_FAST) + 1)
+
+#if I2C_FAST
+#define I2C_TRISE      I2C_TRISE_FAST
+#define I2C_CCR_HIGH   I2C_CCR_HIGH_FAST
+#else
+#define I2C_TRISE      I2C_TRISE_SLOW
+#define I2C_CCR_HIGH   I2C_CCR_HIGH_SLOW
+#endif
+
+#define AO_STM_I2C_CR2_FREQ    (AO_APB1CLK / 1000000)
+
+#define AO_STM_I2C_CR1 ((0 << STM_I2C_CR1_SWRST) |     \
+                       (0 << STM_I2C_CR1_ALERT) |      \
+                       (0 << STM_I2C_CR1_PEC) |        \
+                       (0 << STM_I2C_CR1_POS) |        \
+                       (0 << STM_I2C_CR1_ACK) |        \
+                       (0 << STM_I2C_CR1_STOP) |       \
+                       (0 << STM_I2C_CR1_START) |      \
+                       (0 << STM_I2C_CR1_NOSTRETCH) |  \
+                       (0 << STM_I2C_CR1_ENGC) |       \
+                       (0 << STM_I2C_CR1_ENPEC) |      \
+                       (0 << STM_I2C_CR1_ENARP) |      \
+                       (0 << STM_I2C_CR1_SMBTYPE) |    \
+                       (0 << STM_I2C_CR1_SMBUS) |      \
+                       (1 << STM_I2C_CR1_PE))
+
+#define AO_STM_I2C_CR2  ((0 << STM_I2C_CR2_LAST) |                     \
+                        (0 << STM_I2C_CR2_DMAEN) |                     \
+                        (0 << STM_I2C_CR2_ITBUFEN) |                   \
+                        (0 << STM_I2C_CR2_ITEVTEN) |                   \
+                        (0 << STM_I2C_CR2_ITERREN) |                   \
+                        (AO_STM_I2C_CR2_FREQ << STM_I2C_CR2_FREQ))
+
+static const struct ao_i2c_stm_info    ao_i2c_stm_info[STM_NUM_I2C] = {
+       {
+               .tx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C1_TX),
+               .rx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C1_RX),
+               .stm_i2c = &stm_i2c1
+       },
+       {
+               .tx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C2_TX),
+               .rx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C2_RX),
+               .stm_i2c = &stm_i2c2
+       },
+};
+
+static uint8_t *ao_i2c_recv_data[STM_NUM_I2C];
+static uint16_t        ao_i2c_recv_len[STM_NUM_I2C];
+static uint16_t        ev_count;
+
+static void
+ao_i2c_ev_isr(uint8_t index)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       uint32_t        sr1;
+
+       ++ev_count;
+       sr1 = stm_i2c->sr1;
+       if (sr1 & (1 << STM_I2C_SR1_SB))
+               stm_i2c->dr = ao_i2c_addr[index];
+       if (sr1 & (1 << STM_I2C_SR1_ADDR)) {
+               stm_i2c->cr2 &= ~(1UL << STM_I2C_CR2_ITEVTEN);
+               ao_i2c_state[index] = I2C_RUNNING;
+               ao_wakeup(&ao_i2c_state[index]);
+       }
+       if (sr1 & (1 << STM_I2C_SR1_BTF)) {
+               stm_i2c->cr2 &= ~(1UL << STM_I2C_CR2_ITEVTEN);
+               ao_wakeup(&ao_i2c_state[index]);
+       }
+       if (sr1 & (1 << STM_I2C_SR1_RXNE)) {
+               if (ao_i2c_recv_len[index]) {
+                       *(ao_i2c_recv_data[index]++) = (uint8_t) stm_i2c->dr;
+                       if (!--ao_i2c_recv_len[index])
+                               ao_wakeup(&ao_i2c_recv_len[index]);
+               }
+       }
+}
+
+void stm_i2c1_ev_isr(void) { ao_i2c_ev_isr(0); }
+void stm_i2c2_ev_isr(void) { ao_i2c_ev_isr(1); }
+
+static void
+ao_i2c_er_isr(uint8_t index)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       uint32_t        sr1;
+
+       sr1 = stm_i2c->sr1;
+       if (sr1 & (1 << STM_I2C_SR1_AF)) {
+               ao_i2c_state[index] = I2C_ERROR;
+               stm_i2c->sr1 = sr1 & ~(1UL << STM_I2C_SR1_AF);
+               ao_wakeup(&ao_i2c_state[index]);
+       }
+}
+
+void stm_i2c1_er_isr(void) { ao_i2c_er_isr(0); }
+void stm_i2c2_er_isr(void) { ao_i2c_er_isr(1); }
+
+void
+ao_i2c_get(uint8_t index)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       ao_mutex_get(&ao_i2c_mutex[index]);
+
+       stm_i2c->sr1 = 0;
+       stm_i2c->sr2 = 0;
+}
+
+void
+ao_i2c_put(uint8_t index)
+{
+       ao_mutex_put(&ao_i2c_mutex[index]);
+}
+
+uint8_t
+ao_i2c_start(uint8_t index, uint16_t addr)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       int             t;
+
+       ao_i2c_state[index] = I2C_IDLE;
+       ao_i2c_addr[index] = addr;
+       stm_i2c->cr2 = AO_STM_I2C_CR2;
+       stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_START);
+       for (t = 0; t < I2C_TIMEOUT; t++) {
+               if (!(stm_i2c->cr1 & (1 << STM_I2C_CR1_START)))
+                       break;
+       }
+       ao_arch_block_interrupts();
+       stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_ITEVTEN) | (1 << STM_I2C_CR2_ITERREN);
+       ao_i2c_ev_isr(index);
+       while (ao_i2c_state[index] == I2C_IDLE)
+               if (ao_sleep_for(&ao_i2c_state[index], AO_MS_TO_TICKS(250)))
+                       break;
+       ao_arch_release_interrupts();
+       return ao_i2c_state[index] == I2C_RUNNING;
+}
+
+static void
+ao_i2c_wait_stop(uint8_t index)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       int     t;
+
+       for (t = 0; t < I2C_TIMEOUT; t++) {
+               if (!(stm_i2c->cr1 & (1 << STM_I2C_CR1_STOP)))
+                       break;
+               ao_yield();
+       }
+       ao_i2c_state[index] = I2C_IDLE;
+}
+
+static void
+ao_i2c_wait_addr(uint8_t index)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       int     t;
+
+       for (t = 0; t < I2C_TIMEOUT; t++)
+               if (!(stm_i2c->sr1 & (1 << STM_I2C_SR1_ADDR)))
+                       break;
+       if (t)
+               printf ("wait_addr %d\n", t);
+}
+
+uint8_t
+ao_i2c_send(void *block, uint16_t len, uint8_t index, uint8_t stop)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       uint8_t         tx_dma_index = ao_i2c_stm_info[index].tx_dma_index;
+       uint8_t         rx_dma_index = ao_i2c_stm_info[index].rx_dma_index;
+
+       /* Clear any pending ADDR bit */
+       (void) stm_i2c->sr2;
+       ao_i2c_wait_addr(index);
+       ao_dma_set_transfer(tx_dma_index,
+                           &stm_i2c->dr,
+                           block,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (1 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+       ao_dma_mutex_get(rx_dma_index);
+       stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_DMAEN);
+
+       ao_dma_start(tx_dma_index);
+       ao_arch_block_interrupts();
+       while (!ao_dma_done[tx_dma_index])
+               if (ao_sleep_for(&ao_dma_done[tx_dma_index], 1 + len))
+                       break;
+       stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_ITEVTEN) | (1 << STM_I2C_CR2_ITERREN);
+       while ((stm_i2c->sr1 & (1 << STM_I2C_SR1_BTF)) == 0)
+               if (ao_sleep_for(&ao_i2c_state[index], 1 + len))
+                       break;
+       stm_i2c->cr2 = AO_STM_I2C_CR2;
+       ao_arch_release_interrupts();
+       if (stop) {
+               stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP);
+               ao_i2c_wait_stop(index);
+       }
+       ao_dma_mutex_put(rx_dma_index);
+       ao_dma_done_transfer(tx_dma_index);
+       return true;
+}
+
+static void
+ao_i2c_recv_dma_isr(int index)
+{
+       int             i;
+       struct stm_i2c  *stm_i2c = NULL;
+
+       for (i = 0; i < STM_NUM_I2C; i++)
+               if (index == ao_i2c_stm_info[i].rx_dma_index) {
+                       stm_i2c = ao_i2c_stm_info[i].stm_i2c;
+                       break;
+               }
+       if (!stm_i2c)
+               return;
+       stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_LAST);
+       ao_dma_done[index] = 1;
+       ao_wakeup(&ao_dma_done[index]);
+}
+
+uint8_t
+ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       uint8_t         ret = true;
+
+       if (len == 0)
+               return true;
+       if (len == 1) {
+               ao_i2c_recv_data[index] = block;
+               ao_i2c_recv_len[index] = 1;
+               stm_i2c->cr1 = AO_STM_I2C_CR1;
+
+               /* Clear any pending ADDR bit */
+               stm_i2c->sr2;
+               ao_i2c_wait_addr(index);
+
+               /* Enable interrupts to transfer the byte */
+               stm_i2c->cr2 = (AO_STM_I2C_CR2 |
+                               (1 << STM_I2C_CR2_ITEVTEN) |
+                               (1 << STM_I2C_CR2_ITERREN) |
+                               (1 << STM_I2C_CR2_ITBUFEN));
+               if (stop)
+                       stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP);
+
+               ao_arch_block_interrupts();
+               while (ao_i2c_recv_len[index])
+                       if (ao_sleep_for(&ao_i2c_recv_len[index], 1))
+                               break;
+               ao_arch_release_interrupts();
+               ret = ao_i2c_recv_len[index] == 0;
+       } else {
+               uint8_t         tx_dma_index = ao_i2c_stm_info[index].tx_dma_index;
+               uint8_t         rx_dma_index = ao_i2c_stm_info[index].rx_dma_index;
+               ao_dma_mutex_get(tx_dma_index);
+               ao_dma_set_transfer(rx_dma_index,
+                                   &stm_i2c->dr,
+                                   block,
+                                   len,
+                                   (0 << STM_DMA_CCR_MEM2MEM) |
+                                   (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
+                                   (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                                   (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                                   (1 << STM_DMA_CCR_MINC) |
+                                   (0 << STM_DMA_CCR_PINC) |
+                                   (0 << STM_DMA_CCR_CIRC) |
+                                   (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+
+               /* XXX ao_i2c_recv_dma_isr hasn't ever been used, so it
+                * doesn't appear to be necessary. Testing with a device
+                * that uses i2c would really be useful here to discover
+                * whether this function is necessary or not.
+                */
+#if 0
+               ao_dma_set_isr(rx_dma_index, ao_i2c_recv_dma_isr);
+#else
+               (void) ao_i2c_recv_dma_isr;
+#endif
+               stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_ACK);
+               stm_i2c->cr2 = AO_STM_I2C_CR2 |
+                       (1 << STM_I2C_CR2_DMAEN) | (1 << STM_I2C_CR2_LAST);
+               /* Clear any pending ADDR bit */
+               (void) stm_i2c->sr2;
+               ao_i2c_wait_addr(index);
+
+               ao_dma_start(rx_dma_index);
+               ao_arch_block_interrupts();
+               while (!ao_dma_done[rx_dma_index])
+                       if (ao_sleep_for(&ao_dma_done[rx_dma_index], len))
+                               break;
+               ao_arch_release_interrupts();
+               ret = ao_dma_done[rx_dma_index];
+               stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP);
+               ao_dma_done_transfer(rx_dma_index);
+               ao_dma_mutex_put(tx_dma_index);
+       }
+       if (stop)
+               ao_i2c_wait_stop(index);
+       return ret;
+}
+
+static void
+ao_i2c_channel_init(uint8_t index)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       int i;
+
+       /* Turn I2C off while configuring */
+       stm_i2c->cr1 = (1 << STM_I2C_CR1_SWRST);
+       for (i = 0; i < 100; i++)
+               asm("nop");
+       stm_i2c->cr1 = 0;
+       stm_i2c->cr2 = AO_STM_I2C_CR2;
+
+       (void) stm_i2c->sr1;
+       (void) stm_i2c->sr2;
+       (void) stm_i2c->dr;
+
+       stm_i2c->sr1 = 0;
+       stm_i2c->sr2 = 0;
+
+       stm_i2c->ccr = ((I2C_FAST << STM_I2C_CCR_FS) |
+                       (0 << STM_I2C_CCR_DUTY) |
+                       (I2C_CCR_HIGH << STM_I2C_CCR_CCR));
+
+       stm_i2c->trise = I2C_TRISE;
+
+       stm_i2c->cr1 = AO_STM_I2C_CR1;
+}
+
+static inline void
+i2c_pin_set(struct stm_gpio *gpio, int pin)
+{
+       ao_enable_port(gpio);
+       stm_gpio_conf(gpio, pin,
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_OPEN_DRAIN);
+}
+
+void
+ao_i2c_init(void)
+{
+#if HAS_I2C_1
+# if I2C_1_PB6_PB7
+       stm_set_afio_mapr(STM_AFIO_MAPR_I2C1_REMAP,
+                         STM_AFIO_MAPR_I2C1_REMAP_PB6_PB7,
+                         STM_AFIO_MAPR_I2C1_REMAP_MASK);
+       i2c_pin_set(&stm_gpiob, 6);
+       i2c_pin_set(&stm_gpiob, 7);
+# else
+#  if I2C_1_PB8_PB9
+       stm_set_afio_mapr(STM_AFIO_MAPR_I2C1_REMAP,
+                         STM_AFIO_MAPR_I2C1_REMAP_PB8_PB9,
+                         STM_AFIO_MAPR_I2C1_REMAP_MASK);
+       i2c_pin_set(&stm_gpiob, 8);
+       i2c_pin_set(&stm_gpiob, 9);
+#  else
+#   error "No I2C_1 port configuration specified"
+#  endif
+# endif
+
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_I2C1EN);
+       ao_i2c_channel_init(0);
+
+       stm_nvic_set_enable(STM_ISR_I2C1_EV_POS);
+       stm_nvic_set_priority(STM_ISR_I2C1_EV_POS, AO_STM_NVIC_MED_PRIORITY);
+       stm_nvic_set_enable(STM_ISR_I2C1_ER_POS);
+       stm_nvic_set_priority(STM_ISR_I2C1_ER_POS, AO_STM_NVIC_MED_PRIORITY);
+#endif
+
+#if HAS_I2C_2
+# if I2C_2_PB10_PB11
+       i2c_pin_set(&stm_gpiob, 10);
+       i2c_pin_set(&stm_gpiob, 11);
+# else
+#  error "No I2C_2 port configuration specified"
+# endif
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_I2C2EN);
+       ao_i2c_channel_init(1);
+
+       stm_nvic_set_enable(STM_ISR_I2C2_EV_POS);
+       stm_nvic_set_priority(STM_ISR_I2C2_EV_POS, AO_STM_NVIC_MED_PRIORITY);
+       stm_nvic_set_enable(STM_ISR_I2C2_ER_POS);
+       stm_nvic_set_priority(STM_ISR_I2C2_ER_POS, AO_STM_NVIC_MED_PRIORITY);
+#endif
+}
diff --git a/src/stm32f1/ao_interrupt.c b/src/stm32f1/ao_interrupt.c
new file mode 100644 (file)
index 0000000..2259aef
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include "stm32f1.h"
+#include <string.h>
+#include <ao_boot.h>
+
+extern void main(void);
+
+/* Interrupt functions */
+
+void stm_halt_isr(void)
+{
+       ao_panic(AO_PANIC_CRASH);
+}
+
+void stm_ignore_isr(void)
+{
+}
+
+#define STRINGIFY(x) #x
+
+#define isr(name) \
+       void __attribute__ ((weak)) stm_ ## name ## _isr(void); \
+       _Pragma(STRINGIFY(weak stm_ ## name ## _isr = stm_ignore_isr))
+
+#define isr_halt(name) \
+       void __attribute__ ((weak)) stm_ ## name ## _isr(void); \
+       _Pragma(STRINGIFY(weak stm_ ## name ## _isr = stm_halt_isr))
+
+isr(nmi);
+isr_halt(hardfault);
+isr_halt(memmanage);
+isr_halt(busfault);
+isr_halt(usagefault);
+isr(svc);
+isr(debugmon);
+isr(pendsv);
+isr(systick);
+isr(wwdg);
+isr(pvd);
+isr(tamper_stamp);
+isr(rtc_wkup);
+isr(flash);
+isr(rcc);
+isr(exti0);
+isr(exti1);
+isr(exti2);
+isr(exti3);
+isr(exti4);
+isr(dma1_channel1);
+isr(dma1_channel2);
+isr(dma1_channel3);
+isr(dma1_channel4);
+isr(dma1_channel5);
+isr(dma1_channel6);
+isr(dma1_channel7);
+isr(adc1_2);
+isr(usb_hp);
+isr(usb_lp);
+isr(can_rx1);
+isr(can_sce);
+isr(exti9_5);
+isr(tim1_brk);
+isr(tim1_up);
+isr(tim1_trg_com);
+isr(tim1_cc);
+isr(tim2);
+isr(tim3);
+isr(tim4);
+isr(i2c1_ev);
+isr(i2c1_er);
+isr(i2c2_ev);
+isr(i2c2_er);
+isr(spi1);
+isr(spi2);
+isr(usart1);
+isr(usart2);
+isr(usart3);
+isr(exti15_10);
+isr(rtc_alarm);
+isr(usb_wakeup);
+isr(tim8_brk);
+isr(tim8_up);
+isr(tim8_trg_com);
+isr(tim8_cc);
+isr(adc3);
+isr(fsmc);
+isr(sdio);
+isr(tim5);
+isr(spi3);
+isr(uart4);
+isr(uart5);
+isr(tim6);
+isr(tim7);
+isr(dma2_channel1);
+isr(dma2_channel2);
+isr(dma2_channel3);
+isr(dma2_channel4_5);
+
+#define i(addr,name)   [(addr)/4] = stm_ ## name ## _isr
+
+extern char __stack[];
+void _start(void) __attribute__((__noreturn__));
+void main(void) __attribute__((__noreturn__));
+void ao_setup(void) __attribute__((constructor));
+
+/* This must be exactly 304 bytes long so that the configuration data
+ * gets loaded at the right place
+ */
+
+__attribute__ ((section(".init")))
+const void * const __interrupt_vector[76] = {
+       [0] = &__stack,
+       [1] = _start,
+       i(0x08, nmi),
+       i(0x0c, hardfault),
+       i(0x10, memmanage),
+       i(0x14, busfault),
+       i(0x18, usagefault),
+       i(0x2c, svc),
+       i(0x30, debugmon),
+       i(0x38, pendsv),
+       i(0x3c, systick),
+       i(0x40, wwdg),
+       i(0x44, pvd),
+       i(0x48, tamper_stamp),
+       i(0x4c, rtc_wkup),
+       i(0x50, flash),
+       i(0x54, rcc),
+       i(0x58, exti0),
+       i(0x5c, exti1),
+       i(0x60, exti2),
+       i(0x64, exti3),
+       i(0x68, exti4),
+       i(0x6c, dma1_channel1),
+       i(0x70, dma1_channel2),
+       i(0x74, dma1_channel3),
+       i(0x78, dma1_channel4),
+       i(0x7c, dma1_channel5),
+       i(0x80, dma1_channel6),
+       i(0x84, dma1_channel7),
+       i(0x88, adc1_2),
+       i(0x8c, usb_hp),
+       i(0x90, usb_lp),
+       i(0x94, can_rx1),
+       i(0x98, can_sce),
+       i(0x9c, exti9_5),
+       i(0xa0, tim1_brk),
+       i(0xa4, tim1_up),
+       i(0xa8, tim1_trg_com),
+       i(0xac, tim1_cc),
+       i(0xb0, tim2),
+       i(0xb4, tim3),
+       i(0xb8, tim4),
+       i(0xbc, i2c1_ev),
+       i(0xc0, i2c1_er),
+       i(0xc4, i2c2_ev),
+       i(0xc8, i2c2_er),
+       i(0xcc, spi1),
+       i(0xd0, spi2),
+       i(0xd4, usart1),
+       i(0xd8, usart2),
+       i(0xdc, usart3),
+       i(0xe0, exti15_10),
+       i(0xe4, rtc_alarm),
+       i(0xe8, usb_wakeup),
+       i(0xec, tim8_brk),
+       i(0xf0, tim8_up),
+       i(0xf4, tim8_trg_com),
+       i(0xf8, tim8_cc),
+       i(0xfc, adc3),
+       i(0x100, fsmc),
+       i(0x104, sdio),
+       i(0x108, tim5),
+       i(0x10c, spi3),
+       i(0x110, uart4),
+       i(0x114, uart5),
+       i(0x118, tim6),
+       i(0x11c, tim7),
+       i(0x120, dma2_channel1),
+       i(0x124, dma2_channel2),
+       i(0x128, dma2_channel3),
+       i(0x12c, dma2_channel4_5),
+};
+
+void __attribute__((constructor)) ao_setup(void) {
+#ifdef AO_BOOT_CHAIN
+       if (ao_boot_check_chain()) {
+#ifdef AO_BOOT_PIN
+               if (ao_boot_check_pin())
+#endif
+               {
+                       ao_boot_chain(AO_BOOT_APPLICATION_BASE);
+               }
+       }
+#endif
+       /* Set interrupt vector table offset */
+       stm_scb.vtor = (uint32_t) &__interrupt_vector;
+}
diff --git a/src/stm32f1/ao_pwm_stm.c b/src/stm32f1/ao_pwm_stm.c
new file mode 100644 (file)
index 0000000..dcf9d0e
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright © 2024 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_pwm.h"
+
+static uint8_t pwm_running;
+
+static uint16_t        pwm_value[NUM_PWM];
+
+static void
+ao_pwm_up(void)
+{
+       if (pwm_running++ == 0) {
+               struct stm_tim234       *tim = &AO_PWM_TIMER;
+
+               tim->ccr1 = 0;
+               tim->ccr2 = 0;
+               tim->ccr3 = 0;
+               tim->ccr4 = 0;
+               tim->arr = PWM_MAX - 1; /* turn on the timer */
+               tim->cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+                           (0 << STM_TIM234_CR1_ARPE) |
+                           (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+                           (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+                           (0 << STM_TIM234_CR1_OPM) |
+                           (0 << STM_TIM234_CR1_URS) |
+                           (0 << STM_TIM234_CR1_UDIS) |
+                           (1 << STM_TIM234_CR1_CEN));
+
+               /* Set the timer running */
+               tim->egr = (1 << STM_TIM234_EGR_UG);
+       }
+}
+
+static void
+ao_pwm_down(void)
+{
+       if (--pwm_running == 0) {
+               struct stm_tim234       *tim = &AO_PWM_TIMER;
+
+               tim->arr = 0;
+               tim->cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+                           (0 << STM_TIM234_CR1_ARPE) |
+                           (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+                           (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+                           (0 << STM_TIM234_CR1_OPM) |
+                           (0 << STM_TIM234_CR1_URS) |
+                           (0 << STM_TIM234_CR1_UDIS) |
+                           (0 << STM_TIM234_CR1_CEN));
+
+               /* Stop the timer */
+               tim->egr = (1 << STM_TIM234_EGR_UG);
+       }
+}
+
+void
+ao_pwm_set(uint8_t pwm, uint16_t value)
+{
+       struct stm_tim234       *tim = &AO_PWM_TIMER;
+
+#if PWM_MAX < UINT16_MAX
+       if (value > PWM_MAX)
+               value = PWM_MAX;
+#endif
+       if (value != 0) {
+               if (pwm_value[pwm] == 0)
+                       ao_pwm_up();
+       }
+       switch (pwm) {
+       case 0:
+               tim->ccr1 = value;
+               break;
+       case 1:
+               tim->ccr2 = value;
+               break;
+       case 2:
+               tim->ccr3 = value;
+               break;
+       case 3:
+               tim->ccr4 = value;
+               break;
+       }
+       if (value == 0) {
+               if (pwm_value[pwm] != 0)
+                       ao_pwm_down();
+       }
+       pwm_value[pwm] = value;
+}
+
+static void
+ao_pwm_cmd(void)
+{
+       uint8_t ch;
+       uint16_t val;
+
+       ch = (uint8_t) ao_cmd_decimal();
+       val = (uint16_t) ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+
+       printf("Set channel %d to %d\n", ch, val);
+       ao_pwm_set(ch, val);
+}
+
+static const struct ao_cmds ao_pwm_cmds[] = {
+       { ao_pwm_cmd,   "P <ch> <val>\0Set PWM ch to val" },
+       { 0, NULL },
+};
+
+void
+ao_pwm_init(void)
+{
+       struct stm_tim234       *tim = &AO_PWM_TIMER;
+
+       stm_rcc.apb1enr |= (1 << AO_PWM_TIMER_ENABLE);
+
+       tim->cr1 = 0;
+       tim->psc = AO_PWM_TIMER_SCALE - 1;
+       tim->cnt = 0;
+       tim->ccer = ((1 << STM_TIM234_CCER_CC1E) |
+                    (0 << STM_TIM234_CCER_CC1P) |
+                    (1 << STM_TIM234_CCER_CC2E) |
+                    (0 << STM_TIM234_CCER_CC2P) |
+                    (1 << STM_TIM234_CCER_CC3E) |
+                    (0 << STM_TIM234_CCER_CC3P) |
+                    (1 << STM_TIM234_CCER_CC4E) |
+                    (0 << STM_TIM234_CCER_CC4P));
+
+       tim->ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
+                     (STM_TIM234_CCMR1_OC2M_PWM_MODE_1 << STM_TIM234_CCMR1_OC2M) |
+                     (0 << STM_TIM234_CCMR1_OC2PE) |
+                     (0 << STM_TIM234_CCMR1_OC2FE) |
+                     (STM_TIM234_CCMR1_CC2S_OUTPUT << STM_TIM234_CCMR1_CC2S) |
+
+                     (0 << STM_TIM234_CCMR1_OC1CE) |
+                     (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M) |
+                     (0 << STM_TIM234_CCMR1_OC1PE) |
+                     (0 << STM_TIM234_CCMR1_OC1FE) |
+                     (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
+
+
+       tim->ccmr2 = ((0 << STM_TIM234_CCMR2_OC4CE) |
+                     (STM_TIM234_CCMR2_OC4M_PWM_MODE_1 << STM_TIM234_CCMR2_OC4M) |
+                     (0 << STM_TIM234_CCMR2_OC4PE) |
+                     (0 << STM_TIM234_CCMR2_OC4FE) |
+                     (STM_TIM234_CCMR2_CC4S_OUTPUT << STM_TIM234_CCMR2_CC4S) |
+
+                     (0 << STM_TIM234_CCMR2_OC3CE) |
+                     (STM_TIM234_CCMR2_OC3M_PWM_MODE_1 << STM_TIM234_CCMR2_OC3M) |
+                     (0 << STM_TIM234_CCMR2_OC3PE) |
+                     (0 << STM_TIM234_CCMR2_OC3FE) |
+                     (STM_TIM234_CCMR2_CC3S_OUTPUT << STM_TIM234_CCMR2_CC3S));
+       tim->egr = 0;
+
+       tim->sr = 0;
+       tim->dier = 0;
+       tim->smcr = 0;
+       tim->cr2 = ((0 << STM_TIM234_CR2_TI1S) |
+                   (STM_TIM234_CR2_MMS_RESET<< STM_TIM234_CR2_MMS) |
+                   (0 << STM_TIM234_CR2_CCDS));
+
+       stm_set_afio_mapr(AO_AFIO_PWM_REMAP,
+                         AO_AFIO_PWM_REMAP_VAL,
+                         AO_AFIO_PWM_REMAP_MASK);
+
+       ao_enable_port(AO_PWM_0_GPIO);
+
+       stm_gpio_conf(AO_PWM_0_GPIO, AO_PWM_0_PIN,
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+#if NUM_PWM > 1
+       stm_gpio_conf(AO_PWM_1_GPIO, AO_PWM_1_PIN,
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+#endif
+#if NUM_PWM > 2
+       stm_gpio_conf(AO_PWM_2_GPIO, AO_PWM_2_PIN,
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+#endif
+#if NUM_PWM > 3
+       stm_gpio_conf(AO_PWM_3_GPIO, AO_PWM_3_PIN,
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+#endif
+       ao_cmd_register(&ao_pwm_cmds[0]);
+}
diff --git a/src/stm32f1/ao_serial_stm.c b/src/stm32f1/ao_serial_stm.c
new file mode 100644 (file)
index 0000000..5bfa6d4
--- /dev/null
@@ -0,0 +1,609 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+
+void
+ao_debug_out(char c)
+{
+       if (c == '\n')
+               ao_debug_out('\r');
+       while (!(stm_usart1.sr & (1 << STM_USART_SR_TXE)));
+       stm_usart1.dr = c;
+}
+
+static volatile uint8_t tx;
+
+static int
+_ao_usart_tx_start(struct ao_stm_usart *usart)
+{
+       if (!ao_fifo_empty(usart->tx_fifo)) {
+#if HAS_SERIAL_SW_FLOW
+               if (usart->gpio_cts && ao_gpio_get(usart->gpio_cts, usart->pin_cts) == 1) {
+                       ao_exti_enable(usart->gpio_cts, usart->pin_cts);
+                       return 0;
+               }
+#endif
+               if (usart->reg->sr & (1 << STM_USART_SR_TXE))
+               {
+                       usart->tx_running = 1;
+                       usart->reg->cr1 |= (1 << STM_USART_CR1_TXEIE) | (1 << STM_USART_CR1_TCIE);
+                       ao_fifo_remove(usart->tx_fifo, tx);
+                       usart->reg->dr = tx;
+                       ao_wakeup(&usart->tx_fifo);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+#if HAS_SERIAL_SW_FLOW
+static void
+_ao_usart_cts(struct ao_stm_usart *usart)
+{
+       if (_ao_usart_tx_start(usart))
+               ao_exti_disable(usart->gpio_cts, usart->pin_cts);
+}
+#endif
+
+static void
+_ao_usart_rx(struct ao_stm_usart *usart, int is_stdin)
+{
+       if (usart->reg->sr & (1 << STM_USART_SR_RXNE)) {
+               if (!ao_fifo_full(usart->rx_fifo)) {
+                       ao_fifo_insert(usart->rx_fifo, (char) usart->reg->dr);
+                       ao_wakeup(&usart->rx_fifo);
+                       if (is_stdin)
+                               ao_wakeup(&ao_stdin_ready);
+#if HAS_SERIAL_SW_FLOW
+                       /* If the fifo is nearly full, turn off RTS and wait
+                        * for it to drain a bunch
+                        */
+                       if (usart->gpio_rts && ao_fifo_mostly(usart->rx_fifo)) {
+                               ao_gpio_set(usart->gpio_rts, usart->pin_rts, 1);
+                               usart->rts = 0;
+                       }
+#endif
+               } else {
+                       usart->reg->cr1 &= ~(1UL << STM_USART_CR1_RXNEIE);
+               }
+       }
+}
+
+static void
+ao_usart_isr(struct ao_stm_usart *usart, int is_stdin)
+{
+       _ao_usart_rx(usart, is_stdin);
+
+       if (!_ao_usart_tx_start(usart))
+               usart->reg->cr1 &= ~(1UL << STM_USART_CR1_TXEIE);
+
+       if (usart->reg->sr & (1 << STM_USART_SR_TC)) {
+               usart->tx_running = 0;
+               usart->reg->cr1 &= ~(1UL << STM_USART_CR1_TCIE);
+               if (usart->draining) {
+                       usart->draining = 0;
+                       ao_wakeup(&usart->tx_fifo);
+               }
+       }
+}
+
+static int
+_ao_usart_pollchar(struct ao_stm_usart *usart)
+{
+       int     c;
+
+       if (ao_fifo_empty(usart->rx_fifo))
+               c = AO_READ_AGAIN;
+       else {
+               uint8_t u;
+               ao_fifo_remove(usart->rx_fifo,u);
+               if ((usart->reg->cr1 & (1 << STM_USART_CR1_RXNEIE)) == 0) {
+                       if (ao_fifo_barely(usart->rx_fifo))
+                               usart->reg->cr1 |= (1 << STM_USART_CR1_RXNEIE);
+               }
+#if HAS_SERIAL_SW_FLOW
+               /* If we've cleared RTS, check if there's space now and turn it back on */
+               if (usart->gpio_rts && usart->rts == 0 && ao_fifo_barely(usart->rx_fifo)) {
+                       ao_gpio_set(usart->gpio_rts, usart->pin_rts, 0);
+                       usart->rts = 1;
+               }
+#endif
+               c = u;
+       }
+       return c;
+}
+
+static char
+ao_usart_getchar(struct ao_stm_usart *usart)
+{
+       int c;
+       ao_arch_block_interrupts();
+       while ((c = _ao_usart_pollchar(usart)) == AO_READ_AGAIN)
+               ao_sleep(&usart->rx_fifo);
+       ao_arch_release_interrupts();
+       return (char) c;
+}
+
+static inline uint8_t
+_ao_usart_sleep_for(struct ao_stm_usart *usart, AO_TICK_TYPE timeout)
+{
+       return ao_sleep_for(&usart->rx_fifo, timeout);
+}
+
+static void
+ao_usart_putchar(struct ao_stm_usart *usart, char c)
+{
+       ao_arch_block_interrupts();
+       while (ao_fifo_full(usart->tx_fifo))
+               ao_sleep(&usart->tx_fifo);
+       ao_fifo_insert(usart->tx_fifo, c);
+       _ao_usart_tx_start(usart);
+       ao_arch_release_interrupts();
+}
+
+static void
+ao_usart_drain(struct ao_stm_usart *usart)
+{
+       ao_arch_block_interrupts();
+       while (!ao_fifo_empty(usart->tx_fifo) || usart->tx_running) {
+               usart->draining = 1;
+               ao_sleep(&usart->tx_fifo);
+       }
+       ao_arch_release_interrupts();
+}
+
+static const struct {
+       uint32_t baud;
+} ao_usart_speeds[] = {
+       [AO_SERIAL_SPEED_4800] = {
+               4800
+       },
+       [AO_SERIAL_SPEED_9600] = {
+               9600
+       },
+       [AO_SERIAL_SPEED_19200] = {
+               19200
+       },
+       [AO_SERIAL_SPEED_57600] = {
+               57600
+       },
+       [AO_SERIAL_SPEED_115200] = {
+               115200
+       },
+};
+
+static void
+ao_usart_set_speed(struct ao_stm_usart *usart, uint8_t speed)
+{
+       uint32_t        brr;
+
+       if (speed > AO_SERIAL_SPEED_115200)
+               return;
+#if HAS_SERIAL_1
+       if (usart == &ao_stm_usart1)
+               brr = AO_PCLK2 / ao_usart_speeds[speed].baud;
+       else
+#endif
+               brr = AO_PCLK1 / ao_usart_speeds[speed].baud;
+       usart->reg->brr = brr;
+}
+
+static void
+ao_usart_init(struct ao_stm_usart *usart, int hw_flow, uint8_t speed)
+{
+       usart->reg->cr1 = ((1 << STM_USART_CR1_UE) |
+                         (0 << STM_USART_CR1_M) |
+                         (0 << STM_USART_CR1_WAKE) |
+                         (0 << STM_USART_CR1_PCE) |
+                         (0 << STM_USART_CR1_PS) |
+                         (0 << STM_USART_CR1_PEIE) |
+                         (0 << STM_USART_CR1_TXEIE) |
+                         (0 << STM_USART_CR1_TCIE) |
+                         (1 << STM_USART_CR1_RXNEIE) |
+                         (0 << STM_USART_CR1_IDLEIE) |
+                         (1 << STM_USART_CR1_TE) |
+                         (1 << STM_USART_CR1_RE) |
+                         (0 << STM_USART_CR1_RWU) |
+                         (0 << STM_USART_CR1_SBK));
+
+       usart->reg->cr2 = ((0 << STM_USART_CR2_LINEN) |
+                         (STM_USART_CR2_STOP_1 << STM_USART_CR2_STOP) |
+                         (0 << STM_USART_CR2_CLKEN) |
+                         (0 << STM_USART_CR2_CPOL) |
+                         (0 << STM_USART_CR2_CPHA) |
+                         (0 << STM_USART_CR2_LBCL) |
+                         (0 << STM_USART_CR2_LBDIE) |
+                         (0 << STM_USART_CR2_LBDL) |
+                         (0 << STM_USART_CR2_ADD));
+
+       usart->reg->cr3 = ((0 << STM_USART_CR3_CTSIE) |
+                         (0 << STM_USART_CR3_CTSE) |
+                         (0 << STM_USART_CR3_RTSE) |
+                         (0 << STM_USART_CR3_DMAT) |
+                         (0 << STM_USART_CR3_DMAR) |
+                         (0 << STM_USART_CR3_SCEN) |
+                         (0 << STM_USART_CR3_NACK) |
+                         (0 << STM_USART_CR3_HDSEL) |
+                         (0 << STM_USART_CR3_IRLP) |
+                         (0 << STM_USART_CR3_IREN) |
+                         (0 << STM_USART_CR3_EIE));
+
+       if (hw_flow)
+               usart->reg->cr3 |= ((1 << STM_USART_CR3_CTSE) |
+                                   (1 << STM_USART_CR3_RTSE));
+
+       ao_usart_set_speed(usart, speed);
+}
+
+#if HAS_SERIAL_1
+
+struct ao_stm_usart ao_stm_usart1;
+
+void stm_usart1_isr(void) { ao_usart_isr(&ao_stm_usart1, USE_SERIAL_1_STDIN); }
+
+char
+ao_serial1_getchar(void)
+{
+       return ao_usart_getchar(&ao_stm_usart1);
+}
+
+void
+ao_serial1_putchar(char c)
+{
+       ao_usart_putchar(&ao_stm_usart1, c);
+}
+
+int
+_ao_serial1_pollchar(void)
+{
+       return _ao_usart_pollchar(&ao_stm_usart1);
+}
+
+uint8_t
+_ao_serial1_sleep_for(AO_TICK_TYPE timeout)
+{
+       return _ao_usart_sleep_for(&ao_stm_usart1, timeout);
+}
+
+void
+ao_serial1_drain(void)
+{
+       ao_usart_drain(&ao_stm_usart1);
+}
+
+void
+ao_serial1_set_speed(uint8_t speed)
+{
+       ao_usart_drain(&ao_stm_usart1);
+       ao_usart_set_speed(&ao_stm_usart1, speed);
+}
+#endif /* HAS_SERIAL_1 */
+
+#if HAS_SERIAL_2
+
+struct ao_stm_usart ao_stm_usart2;
+
+void stm_usart2_isr(void) { ao_usart_isr(&ao_stm_usart2, USE_SERIAL_2_STDIN); }
+
+char
+ao_serial2_getchar(void)
+{
+       return ao_usart_getchar(&ao_stm_usart2);
+}
+
+void
+ao_serial2_putchar(char c)
+{
+       ao_usart_putchar(&ao_stm_usart2, c);
+}
+
+int
+_ao_serial2_pollchar(void)
+{
+       return _ao_usart_pollchar(&ao_stm_usart2);
+}
+
+uint8_t
+_ao_serial2_sleep_for(AO_TICK_TYPE timeout)
+{
+       return _ao_usart_sleep_for(&ao_stm_usart2, timeout);
+}
+
+void
+ao_serial2_drain(void)
+{
+       ao_usart_drain(&ao_stm_usart2);
+}
+
+void
+ao_serial2_set_speed(uint8_t speed)
+{
+       ao_usart_drain(&ao_stm_usart2);
+       ao_usart_set_speed(&ao_stm_usart2, speed);
+}
+
+#if HAS_SERIAL_SW_FLOW
+static void
+ao_serial2_cts(void)
+{
+       _ao_usart_cts(&ao_stm_usart2);
+}
+#endif
+
+#endif /* HAS_SERIAL_2 */
+
+#if HAS_SERIAL_3
+
+struct ao_stm_usart ao_stm_usart3;
+
+void stm_usart3_isr(void) { ao_usart_isr(&ao_stm_usart3, USE_SERIAL_3_STDIN); }
+
+char
+ao_serial3_getchar(void)
+{
+       return ao_usart_getchar(&ao_stm_usart3);
+}
+
+void
+ao_serial3_putchar(char c)
+{
+       ao_usart_putchar(&ao_stm_usart3, c);
+}
+
+int
+_ao_serial3_pollchar(void)
+{
+       return _ao_usart_pollchar(&ao_stm_usart3);
+}
+
+uint8_t
+_ao_serial3_sleep_for(AO_TICK_TYPE timeout)
+{
+       return _ao_usart_sleep_for(&ao_stm_usart3, timeout);
+}
+
+void
+ao_serial3_set_speed(uint8_t speed)
+{
+       ao_usart_drain(&ao_stm_usart3);
+       ao_usart_set_speed(&ao_stm_usart3, speed);
+}
+
+void
+ao_serial3_drain(void)
+{
+       ao_usart_drain(&ao_stm_usart3);
+}
+#endif /* HAS_SERIAL_3 */
+
+#if HAS_SERIAL_SW_FLOW
+static void
+ao_serial_set_sw_rts_cts(struct ao_stm_usart *usart,
+                        void (*isr)(void),
+                        struct stm_gpio *port_rts,
+                        uint8_t pin_rts,
+                        struct stm_gpio *port_cts,
+                        uint8_t pin_cts)
+{
+       /* Pull RTS low to note that there's space in the FIFO
+        */
+       ao_enable_output(port_rts, pin_rts, 0);
+       usart->gpio_rts = port_rts;
+       usart->pin_rts = pin_rts;
+       usart->rts = 1;
+
+       ao_exti_setup(port_cts, pin_cts, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, isr);
+       usart->gpio_cts = port_cts;
+       usart->pin_cts = pin_cts;
+}
+#endif
+
+void
+ao_serial_init(void)
+{
+#if HAS_SERIAL_1
+       /*
+        *      TX      RX
+        *      PA9     PA10
+        *      PB6     PB7     *
+        */
+
+#if SERIAL_1_PA9_PA10
+       ao_enable_port(&stm_gpioa);
+       stm_gpio_conf(&stm_gpioa, 9,
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+       stm_gpio_conf(&stm_gpioa, 10,
+                     STM_GPIO_CR_MODE_INPUT,
+                     STM_GPIO_CR_CNF_INPUT_FLOATING);
+
+       stm_set_afio_mapr(STM_AFIO_MAPR_USART1_REMAP,
+                         STM_AFIO_MAPR_USART1_REMAP_PA9_PA10,
+                         STM_AFIO_MAPR_USART1_REMAP_MASK);
+#else
+#if SERIAL_1_PB6_PB7
+       ao_enable_port(&stm_gpiob);
+       stm_gpio_conf(&stm_gpiob, 6,
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+       stm_gpio_conf(&stm_gpiob, 7,
+                     STM_GPIO_CR_MODE_INPUT,
+                     STM_GPIO_CR_CNF_INPUT_FLOATING);
+
+       stm_set_afio_mapr(STM_AFIO_MAPR_USART1_REMAP,
+                         STM_AFIO_MAPR_USART1_REMAP_PB6_PB7,
+                         STM_AFIO_MAPR_USART1_REMAP_MASK);
+#else
+#error "No SERIAL_1 port configuration specified"
+#endif
+#endif
+       /* Enable USART */
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_USART1EN);
+
+       ao_stm_usart1.reg = &stm_usart1;
+       ao_usart_init(&ao_stm_usart1, 0);
+
+       stm_nvic_set_enable(STM_ISR_USART1_POS);
+       stm_nvic_set_priority(STM_ISR_USART1_POS, AO_STM_NVIC_MED_PRIORITY);
+#if USE_SERIAL_1_STDIN && !DELAY_SERIAL_1_STDIN
+       ao_add_stdio(_ao_serial1_pollchar,
+                    ao_serial1_putchar,
+                    NULL);
+#endif
+#endif
+
+#if HAS_SERIAL_2
+       /*
+        *      TX      RX
+        *      PA2     PA3
+        *      PD5     PD6
+        */
+
+#if SERIAL_2_PA2_PA3
+       ao_enable_port(&stm_gpioa);
+       stm_gpio_conf(&stm_gpioa, 2,
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+       stm_gpio_conf(&stm_gpioa, 3,
+                     STM_GPIO_CR_MODE_INPUT,
+                     STM_GPIO_CR_CNF_INPUT_FLOATING);
+
+#ifndef USE_SERIAL_2_FLOW
+#define USE_SERIAL_2_FLOW      0
+#define USE_SERIAL_2_SW_FLOW   0
+#endif
+
+# if USE_SERIAL_2_FLOW
+#  if USE_SERIAL_2_SW_FLOW
+       ao_serial_set_sw_rts_cts(&ao_stm_usart2,
+                                ao_serial2_cts,
+                                SERIAL_2_PORT_RTS,
+                                SERIAL_2_PIN_RTS,
+                                SERIAL_2_PORT_CTS,
+                                SERIAL_2_PIN_CTS);
+#  else
+       stm_gpio_conf(&stm_gpioa, 0,                    /* CTS */
+                     STM_GPIO_CR_MODE_INPUT,
+                     STM_GPIO_CR_CNF_INPUT_FLOATING);
+       stm_gpio_conf(&stm_gpioa, 1,                    /* RTS */
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+#  endif
+# endif
+       stm_set_afio_mapr(STM_AFIO_MAPR_USART2_REMAP,
+                         STM_AFIO_MAPR_USART2_REMAP_PA0_PA1_PA2_PA3_PA4,
+                         STM_AFIO_MAPR_USART2_REMAP_MASK);
+#elif SERIAL_2_PD5_PD6
+       ao_enable_port(&stm_gpiod);
+       stm_gpio_conf(&stm_gpiod, 5,
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+       stm_gpio_conf(&stm_gpiod, 6,
+                     STM_GPIO_CR_MODE_INPUT,
+                     STM_GPIO_CR_CNF_INPUT_FLOATING);
+
+# if USE_SERIAL_2_FLOW
+#  if USE_SERIAL_2_SW_FLOW
+       ao_serial_set_sw_rts_cts(&ao_stm_usart2,
+                                ao_serial2_cts,
+                                SERIAL_2_PORT_RTS,
+                                SERIAL_2_PIN_RTS,
+                                SERIAL_2_PORT_CTS,
+                                SERIAL_2_PIN_CTS);
+#  else
+       stm_gpio_conf(&stm_gpiod, 3,                    /* CTS */
+                     STM_GPIO_CR_MODE_INPUT,
+                     STM_GPIO_CR_CNF_INPUT_FLOATING);
+       stm_gpio_conf(&stm_gpiod, 4,                    /* RTS */
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+#  endif
+# endif
+       stm_set_afio_mapr(STM_AFIO_MAPR_USART2_REMAP,
+                         STM_AFIO_MAPR_USART2_REMAP_PD3_PD4_PD5_PD6_PD7,
+                         STM_AFIO_MAPR_USART2_REMAP_MASK);
+#else
+#error "No SERIAL_2 port configuration specified"
+#endif
+       /* Enable USART */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART2EN);
+
+       ao_stm_usart2.reg = &stm_usart2;
+       ao_usart_init(&ao_stm_usart2, USE_SERIAL_2_FLOW && !USE_SERIAL_2_SW_FLOW, SERIAL_2_SPEED);
+
+       stm_nvic_set_enable(STM_ISR_USART2_POS);
+       stm_nvic_set_priority(STM_ISR_USART2_POS, AO_STM_NVIC_MED_PRIORITY);
+#if USE_SERIAL_2_STDIN && !DELAY_SERIAL_2_STDIN
+       ao_add_stdio(_ao_serial2_pollchar,
+                    ao_serial2_putchar,
+                    NULL);
+#endif
+#endif
+
+#if HAS_SERIAL_3
+       /*
+        *      TX      RX
+        *      PB10    PB11
+        *      PC10    PC11
+        *      PD8     PD9
+        */
+#if SERIAL_3_PB10_PB11
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+
+       stm_afr_set(&stm_gpiob, 10, STM_AFR_AF7);
+       stm_afr_set(&stm_gpiob, 11, STM_AFR_AF7);
+#else
+#if SERIAL_3_PC10_PC11
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOCEN);
+
+       stm_afr_set(&stm_gpioc, 10, STM_AFR_AF7);
+       stm_afr_set(&stm_gpioc, 11, STM_AFR_AF7);
+#else
+#if SERIAL_3_PD8_PD9
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
+
+       stm_afr_set(&stm_gpiod, 8, STM_AFR_AF7);
+       stm_afr_set(&stm_gpiod, 9, STM_AFR_AF7);
+#else
+#error "No SERIAL_3 port configuration specified"
+#endif
+#endif
+#endif
+       /* Enable USART */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART3EN);
+
+       ao_stm_usart3.reg = &stm_usart3;
+       ao_usart_init(&ao_stm_usart3, 0);
+
+       stm_nvic_set_enable(STM_ISR_USART3_POS);
+       stm_nvic_set_priority(STM_ISR_USART3_POS, AO_STM_NVIC_MED_PRIORITY);
+#if USE_SERIAL_3_STDIN && !DELAY_SERIAL_3_STDIN
+       ao_add_stdio(_ao_serial3_pollchar,
+                    ao_serial3_putchar,
+                    NULL);
+#endif
+#endif
+}
diff --git a/src/stm32f1/ao_spi_stm.c b/src/stm32f1/ao_spi_stm.c
new file mode 100644 (file)
index 0000000..2c2fcd3
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+
+struct ao_spi_stm_info {
+       uint8_t miso_dma_index;
+       uint8_t mosi_dma_index;
+       struct stm_spi *stm_spi;
+};
+
+static uint8_t         ao_spi_mutex[STM_NUM_SPI];
+static uint8_t         ao_spi_pin_config[STM_NUM_SPI];
+
+static const struct ao_spi_stm_info ao_spi_stm_info[STM_NUM_SPI] = {
+       {
+               .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_RX),
+               .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX),
+               &stm_spi1
+       },
+       {
+               .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_RX),
+               .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_TX),
+               &stm_spi2
+       }
+};
+
+static uint8_t spi_dev_null;
+
+#if DEBUG
+static struct {
+       uint8_t task;
+       uint8_t which;
+       AO_TICK_TYPE tick;
+       uint16_t len;
+} spi_tasks[64];
+static uint8_t spi_task_index;
+
+static void
+validate_spi(struct stm_spi *stm_spi, int which, uint16_t len)
+{
+       uint32_t        sr = stm_spi->sr;
+
+       if (stm_spi != &stm_spi2)
+               return;
+       spi_tasks[spi_task_index].task = ao_cur_task ? ao_cur_task->task_id : 0;
+       spi_tasks[spi_task_index].which = which;
+       spi_tasks[spi_task_index].tick = ao_time();
+       spi_tasks[spi_task_index].len = len;
+       spi_task_index = (spi_task_index + 1) & (63);
+       if (sr & (1 << STM_SPI_SR_FRE))
+               ao_panic(0x40 | 1);
+       if (sr & (1 << STM_SPI_SR_BSY))
+               ao_panic(0x40 | 2);
+       if (sr & (1 << STM_SPI_SR_OVR))
+               ao_panic(0x40 | 3);
+       if (sr & (1 << STM_SPI_SR_MODF))
+               ao_panic(0x40 | 4);
+       if (sr & (1 << STM_SPI_SR_UDR))
+               ao_panic(0x40 | 5);
+       if ((sr & (1 << STM_SPI_SR_TXE)) == 0)
+               ao_panic(0x40 | 6);
+       if (sr & (1 << STM_SPI_SR_RXNE))
+               ao_panic(0x40 | 7);
+       if (which != 5 && which != 6 && which != 13)
+               if (ao_cur_task->task_id != ao_spi_mutex[1])
+                       ao_panic(0x40 | 8);
+}
+#else
+#define validate_spi(stm_spi, which, len) do { (void) (which); (void) (len); } while (0)
+#endif
+
+static void
+ao_spi_set_dma_mosi(uint8_t id, const void *data, uint16_t len, uint32_t minc)
+{
+       struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+       uint8_t mosi_dma_index = ao_spi_stm_info[id].mosi_dma_index;
+
+       ao_dma_set_transfer(mosi_dma_index,
+                           &stm_spi->dr,
+                           (void *) data,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (minc << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+}
+
+static void
+ao_spi_set_dma_miso(uint8_t id, void *data, uint16_t len, uint32_t minc)
+{
+       struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+       uint8_t miso_dma_index = ao_spi_stm_info[id].miso_dma_index;
+
+       ao_dma_set_transfer(miso_dma_index,
+                           &stm_spi->dr,
+                           data,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_VERY_HIGH << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (minc << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+}
+
+static void
+ao_spi_run(uint8_t id, uint8_t which, uint16_t len)
+{
+       struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+       uint8_t mosi_dma_index = ao_spi_stm_info[id].mosi_dma_index;
+       uint8_t miso_dma_index = ao_spi_stm_info[id].miso_dma_index;
+
+       validate_spi(stm_spi, which, len);
+
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (1 << STM_SPI_CR2_TXDMAEN) |
+                       (1 << STM_SPI_CR2_RXDMAEN));
+
+       ao_dma_start(miso_dma_index);
+       ao_dma_start(mosi_dma_index);
+
+       ao_arch_critical(
+               while (!ao_dma_done[miso_dma_index])
+                       ao_sleep(&ao_dma_done[miso_dma_index]);
+               );
+
+       while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0);
+       while (stm_spi->sr & (1 << STM_SPI_SR_BSY));
+
+       validate_spi(stm_spi, which+1, len);
+
+       stm_spi->cr2 = 0;
+
+       ao_dma_done_transfer(mosi_dma_index);
+       ao_dma_done_transfer(miso_dma_index);
+}
+
+void
+ao_spi_send(const void *block, uint16_t len, uint8_t spi_index)
+{
+       uint8_t id = AO_SPI_INDEX(spi_index);
+
+       /* Set up the transmit DMA to deliver data */
+       ao_spi_set_dma_mosi(id, block, len, 1);
+
+       /* Set up the receive DMA -- when this is done, we know the SPI unit
+        * is idle. Without this, we'd have to poll waiting for the BSY bit to
+        * be cleared
+        */
+       ao_spi_set_dma_miso(id, &spi_dev_null, len, 0);
+
+       ao_spi_run(id, 1, len);
+}
+
+void
+ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index)
+{
+       uint8_t id = AO_SPI_INDEX(spi_index);
+
+       /* Set up the transmit DMA to deliver data */
+       ao_spi_set_dma_mosi(id, &value, len, 0);
+
+       /* Set up the receive DMA -- when this is done, we know the SPI unit
+        * is idle. Without this, we'd have to poll waiting for the BSY bit to
+        * be cleared
+        */
+       ao_spi_set_dma_miso(id, &spi_dev_null, len, 0);
+
+       ao_spi_run(id, 3, len);
+}
+
+void
+ao_spi_start_bytes(uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+       struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (0 << STM_SPI_CR2_TXDMAEN) |
+                       (0 << STM_SPI_CR2_RXDMAEN));
+       validate_spi(stm_spi, 5, 0xffff);
+}
+
+void
+ao_spi_stop_bytes(uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+       struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+       while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0)
+               ;
+       while (stm_spi->sr & (1 << STM_SPI_SR_BSY))
+               ;
+       /* Clear the OVR flag */
+       (void) stm_spi->dr;
+       (void) stm_spi->sr;
+       validate_spi(stm_spi, 6, 0xffff);
+       stm_spi->cr2 = 0;
+}
+
+void
+ao_spi_send_sync(const void *block, uint16_t len, uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+       const uint8_t   *b = block;
+       struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (0 << STM_SPI_CR2_TXDMAEN) |
+                       (0 << STM_SPI_CR2_RXDMAEN));
+       validate_spi(stm_spi, 7, len);
+       while (len--) {
+               while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE)));
+               stm_spi->dr = *b++;
+       }
+       while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0)
+               ;
+       while (stm_spi->sr & (1 << STM_SPI_SR_BSY))
+               ;
+       /* Clear the OVR flag */
+       (void) stm_spi->dr;
+       (void) stm_spi->sr;
+       validate_spi(stm_spi, 8, len);
+}
+
+void
+ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+
+       spi_dev_null = 0xff;
+
+       /* Set up transmit DMA to make the SPI hardware actually run */
+       ao_spi_set_dma_mosi(id, &spi_dev_null, len, 0);
+
+       /* Set up the receive DMA to capture data */
+       ao_spi_set_dma_miso(id, block, len, 1);
+
+       ao_spi_run(id, 9, len);
+}
+
+void
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+
+       /* Set up transmit DMA to send data */
+       ao_spi_set_dma_mosi(id, out, len, 1);
+
+       /* Set up the receive DMA to capture data */
+       ao_spi_set_dma_miso(id, in, len, 1);
+
+       ao_spi_run(id, 11, len);
+}
+
+#define stm_spi_input_disable(gpio, pin) do {                  \
+               stm_gpio_conf(gpio, pin,                        \
+                             STM_GPIO_CR_MODE_INPUT,           \
+                             STM_GPIO_CR_CNF_INPUT_FLOATING);  \
+       } while(0)
+
+#define stm_spi_output_disable(gpio, pin, mode) do {                   \
+               ao_gpio_set(gpio, pin, 1);                              \
+               stm_gpio_conf(gpio, pin,                                \
+                             mode,                                     \
+                             STM_GPIO_CR_CNF_OUTPUT_PUSH_PULL);        \
+       } while(0)
+
+static void
+ao_spi_disable_pin_config(uint8_t spi_pin_config)
+{
+       /* Disable current config
+        */
+       switch (spi_pin_config) {
+#if SPI_1_PA5_PA6_PA7
+       case AO_SPI_1_PA5_PA6_PA7:
+               stm_spi_output_disable(&stm_gpioa, 5, SPI_1_MODE_OUTPUT);
+#ifndef SPI_1_PA6_DISABLE
+               stm_spi_input_disable(&stm_gpioa, 6);
+#endif
+               stm_spi_output_disable(&stm_gpioa, 7, SPI_1_MODE_OUTPUT);
+               break;
+#endif
+#if SPI_1_PB3_PB4_PB5
+       case AO_SPI_1_PB3_PB4_PB5:
+               stm_spi_output_disable(&stm_gpiob, 3, SPI_1_MODE_OUTPUT);
+               stm_spi_input_disable(&stm_gpiob, 4);
+               stm_spi_output_disable(&stm_gpiob, 5, SPI_1_MODE_OUTPUT);
+               break;
+#endif
+#if SPI_2_PB13_PB14_PB15
+       case AO_SPI_2_PB13_PB14_PB15:
+               stm_spi_output_disable(&stm_gpiob, 13, SPI_2_MODE_OUTPUT);
+               stm_spi_input_disable(&stm_gpiob, 14);
+               stm_spi_output_disable(&stm_gpiob, 15, SPI_2_MODE_OUTPUT);
+               break;
+#endif
+       }
+}
+
+#define stm_spi_input_enable(gpio, pin) do {                   \
+               stm_gpio_conf(gpio, pin,                        \
+                             STM_GPIO_CR_MODE_INPUT,           \
+                             STM_GPIO_CR_CNF_INPUT_FLOATING);  \
+       } while(0)
+
+#define stm_spi_output_enable(gpio, pin, mode) do {                    \
+               stm_gpio_conf(gpio, pin,                                \
+                             mode,                                     \
+                             STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);     \
+       } while(0)
+
+static void
+ao_spi_enable_pin_config(uint8_t spi_pin_config)
+{
+       /* Enable new config
+        */
+       switch (spi_pin_config) {
+#if SPI_1_PA5_PA6_PA7
+       case AO_SPI_1_PA5_PA6_PA7:
+               stm_set_afio_mapr(STM_AFIO_MAPR_SPI1_REMAP,
+                                 STM_AFIO_MAPR_SPI1_REMAP_PA4_PA5_PA6_PA7,
+                                 STM_AFIO_MAPR_SPI1_REMAP_MASK);
+               stm_spi_output_enable(&stm_gpioa, 5, SPI_1_MODE_OUTPUT);
+#ifndef SPI_1_PA6_DISABLE
+               stm_spi_input_enable(&stm_gpioa, 6);
+#endif
+               stm_spi_output_enable(&stm_gpioa, 7, SPI_1_MODE_OUTPUT);
+               break;
+#endif
+#if SPI_1_PB3_PB4_PB5
+       case AO_SPI_1_PB3_PB4_PB5:
+               stm_set_afio_mapr(STM_AFIO_MAPR_SPI1_REMAP,
+                                 STM_AFIO_MAPR_SPI1_REMAP_PA15_PB3_PB4_PB5,
+                                 STM_AFIO_MAPR_SPI1_REMAP_MASK);
+               stm_spi_output_enable(&stm_gpiob, 3, SPI_1_MODE_OUTPUT);
+               stm_spi_input_enable(&stm_gpiob, 4);
+               stm_spi_output_enable(&stm_gpiob, 5, SPI_1_MODE_OUTPUT);
+               break;
+#endif
+#if SPI_2_PB13_PB14_PB15
+       case AO_SPI_2_PB13_PB14_PB15:
+               stm_spi_output_enable(&stm_gpiob, 13, SPI_2_MODE_OUTPUT);
+               stm_spi_input_enable(&stm_gpiob, 14);
+               stm_spi_output_enable(&stm_gpiob, 15, SPI_2_MODE_OUTPUT);
+               break;
+#endif
+       }
+}
+
+static void
+ao_spi_config(uint8_t spi_index, uint32_t speed)
+{
+       uint8_t         spi_pin_config = AO_SPI_PIN_CONFIG(spi_index);
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+       struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+       if (spi_pin_config != ao_spi_pin_config[id]) {
+
+               /* Disable old config
+                */
+               ao_spi_disable_pin_config(ao_spi_pin_config[id]);
+
+               /* Enable new config
+                */
+               ao_spi_enable_pin_config(spi_pin_config);
+
+               /* Remember current config
+                */
+               ao_spi_pin_config[id] = spi_pin_config;
+       }
+
+       /* Turn the SPI transceiver on and set the mode */
+       stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) |           /* Three wire mode */
+                       (0 << STM_SPI_CR1_BIDIOE) |
+                       (0 << STM_SPI_CR1_CRCEN) |              /* CRC disabled */
+                       (0 << STM_SPI_CR1_CRCNEXT) |
+                       (0 << STM_SPI_CR1_DFF) |
+                       (0 << STM_SPI_CR1_RXONLY) |
+                       (1 << STM_SPI_CR1_SSM) |                /* Software SS handling */
+                       (1 << STM_SPI_CR1_SSI) |                /*  ... */
+                       (0 << STM_SPI_CR1_LSBFIRST) |           /* Big endian */
+                       (1 << STM_SPI_CR1_SPE) |                /* Enable SPI unit */
+                       (speed << STM_SPI_CR1_BR) |             /* baud rate to pclk/4 */
+                       (1 << STM_SPI_CR1_MSTR) |
+                       (AO_SPI_CPOL(spi_index) << STM_SPI_CR1_CPOL) |  /* Format */
+                       (AO_SPI_CPHA(spi_index) << STM_SPI_CR1_CPHA));
+       validate_spi(stm_spi, 13, 0);
+}
+
+uint8_t
+ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+
+       if (!ao_mutex_try(&ao_spi_mutex[id], task_id))
+               return 0;
+       ao_spi_config(spi_index, speed);
+       return 1;
+}
+
+void
+ao_spi_get(uint8_t spi_index, uint32_t speed)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+
+       ao_mutex_get(&ao_spi_mutex[id]);
+       ao_spi_config(spi_index, speed);
+}
+
+void
+ao_spi_put(uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+       struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+       stm_spi->cr1 = 0;
+       ao_mutex_put(&ao_spi_mutex[id]);
+}
+
+void
+ao_spi_put_pins(uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+
+       ao_spi_disable_pin_config(ao_spi_pin_config[id]);
+       ao_spi_pin_config[id] = AO_SPI_CONFIG_NONE;
+       ao_spi_put(spi_index);
+}
+
+static void
+ao_spi_channel_init(uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+       struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+       ao_spi_disable_pin_config(AO_SPI_PIN_CONFIG(spi_index));
+
+       stm_spi->cr1 = 0;
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (0 << STM_SPI_CR2_TXDMAEN) |
+                       (0 << STM_SPI_CR2_RXDMAEN));
+
+       /* Clear any pending data and error flags */
+       (void) stm_spi->dr;
+       (void) stm_spi->sr;
+}
+
+#if DEBUG
+void
+ao_spi_dump_cmd(void)
+{
+       int s;
+
+       for (s = 0; s < 64; s++) {
+               int i = (spi_task_index + s) & 63;
+               if (spi_tasks[i].which) {
+                       int t;
+                       const char *name = "(none)";
+                       for (t = 0; t < ao_num_tasks; t++)
+                               if (ao_tasks[t]->task_id == spi_tasks[i].task) {
+                                       name = ao_tasks[t]->name;
+                                       break;
+                               }
+                       printf("%2d: %5d task %2d which %2d len %5d %s\n",
+                              s,
+                              spi_tasks[i].tick,
+                              spi_tasks[i].task,
+                              spi_tasks[i].which,
+                              spi_tasks[i].len,
+                              name);
+               }
+       }
+       for (s = 0; s < STM_NUM_SPI; s++) {
+               struct stm_spi *spi = ao_spi_stm_info[s].stm_spi;
+
+               printf("%1d: mutex %2d index %3d miso dma %3d mosi dma %3d",
+                      s, ao_spi_mutex[s], ao_spi_index[s],
+                      ao_spi_stm_info[s].miso_dma_index,
+                      ao_spi_stm_info[s].mosi_dma_index);
+               printf(" cr1 %04x cr2 %02x sr %03x\n",
+                      spi->cr1, spi->cr2, spi->sr);
+       }
+
+}
+
+static const struct ao_cmds ao_spi_cmds[] = {
+       { ao_spi_dump_cmd,      "S\0Dump SPI status" },
+       { 0, NULL }
+};
+#endif
+
+void
+ao_spi_init(void)
+{
+#if HAS_SPI_1
+# if SPI_1_PA5_PA6_PA7
+       ao_enable_port(&stm_gpioa);
+# endif
+# if SPI_1_PB3_PB4_PB5
+       ao_enable_port(&stm_gpiob);
+# endif
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
+       ao_spi_pin_config[0] = AO_SPI_CONFIG_NONE;
+       ao_spi_channel_init(0);
+#endif
+
+#if HAS_SPI_2
+# if SPI_2_PB13_PB14_PB15
+       ao_enable_port(&stm_gpiob);
+# endif
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
+       ao_spi_pin_config[1] = AO_SPI_CONFIG_NONE;
+       ao_spi_channel_init(1);
+#endif
+#if DEBUG
+       ao_cmd_register(&ao_spi_cmds[0]);
+#endif
+}
diff --git a/src/stm32f1/ao_timer.c b/src/stm32f1/ao_timer.c
new file mode 100644 (file)
index 0000000..1368ab6
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright © 2023 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_task.h>
+
+#ifndef HAS_TICK
+#define HAS_TICK 1
+#endif
+
+volatile AO_TICK_TYPE ao_tick_count;
+
+AO_TICK_TYPE
+ao_time(void)
+{
+       return ao_tick_count;
+}
+
+uint64_t
+ao_time_ns(void)
+{
+       AO_TICK_TYPE    before, after;
+       uint32_t        val;
+
+       do {
+               before = ao_tick_count;
+               val = stm_systick.val;
+               after = ao_tick_count;
+       } while (before != after);
+
+       return (uint64_t) after * (1000000000ULL / AO_HERTZ) +
+               (uint64_t) val * (1000000000ULL / AO_SYSTICK);
+}
+
+#if AO_DATA_ALL
+volatile uint8_t       ao_data_interval = 1;
+volatile uint8_t       ao_data_count;
+#endif
+
+void stm_systick_isr(void)
+{
+       if (stm_systick.ctrl & (1 << STM_SYSTICK_CTRL_COUNTFLAG)) {
+               ++ao_tick_count;
+               ao_task_check_alarm();
+#if AO_DATA_ALL
+               if (++ao_data_count == ao_data_interval && ao_data_interval) {
+                       ao_data_count = 0;
+#if HAS_FAKE_FLIGHT
+                       if (ao_fake_flight_active)
+                               ao_fake_flight_poll();
+                       else
+#endif
+                               ao_adc_poll();
+#if (AO_DATA_ALL & ~(AO_DATA_ADC))
+                       ao_wakeup((void *) &ao_data_count);
+#endif
+               }
+#endif
+#ifdef AO_TIMER_HOOK
+               AO_TIMER_HOOK;
+#endif
+       }
+}
+
+#if HAS_ADC
+void
+ao_timer_set_adc_interval(uint8_t interval)
+{
+       ao_arch_critical(
+               ao_data_interval = interval;
+               ao_data_count = 0;
+               );
+}
+#endif
+
+#define SYSTICK_RELOAD (AO_SYSTICK / 100 - 1)
+
+void
+ao_timer_init(void)
+{
+       stm_systick.load = SYSTICK_RELOAD;
+       stm_systick.val = 0;
+       stm_systick.ctrl = ((1 << STM_SYSTICK_CTRL_ENABLE) |
+                           (1 << STM_SYSTICK_CTRL_TICKINT) |
+                           (STM_SYSTICK_CTRL_CLKSOURCE_HCLK_8 << STM_SYSTICK_CTRL_CLKSOURCE));
+       stm_scb.shpr3 |= (uint32_t) AO_STM_NVIC_CLOCK_PRIORITY << 24;
+}
diff --git a/src/stm32f1/ao_usb_stm.c b/src/stm32f1/ao_usb_stm.c
new file mode 100644 (file)
index 0000000..3d632dd
--- /dev/null
@@ -0,0 +1,1172 @@
+/*
+ * Copyright © 2023 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_usb.h"
+#include "ao_product.h"
+
+#define USB_DEBUG      0
+#define USB_DEBUG_DATA 0
+#define USB_ECHO       0
+
+#ifndef USE_USB_STDIO
+#define USE_USB_STDIO  1
+#endif
+
+#if USE_USB_STDIO
+#define AO_USB_OUT_SLEEP_ADDR  (&ao_stdin_ready)
+#else
+#define AO_USB_OUT_SLEEP_ADDR  (&ao_usb_out_avail)
+#endif
+
+#if USB_DEBUG
+#define debug(format, args...) printf(format, ## args);
+#else
+#define debug(format, args...)
+#endif
+
+#if USB_DEBUG_DATA
+#define debug_data(format, args...)    printf(format, ## args);
+#else
+#define debug_data(format, args...)
+#endif
+
+struct ao_usb_setup {
+       uint8_t         dir_type_recip;
+       uint8_t         request;
+       uint16_t        value;
+       uint16_t        index;
+       uint16_t        length;
+} ao_usb_setup;
+
+static uint8_t         ao_usb_ep0_state;
+
+/* Pending EP0 IN data */
+static const uint8_t   *ao_usb_ep0_in_data;    /* Remaining data */
+static uint8_t                 ao_usb_ep0_in_len;      /* Remaining amount */
+
+/* Temp buffer for smaller EP0 in data */
+static uint8_t ao_usb_ep0_in_buf[2];
+
+/* Pending EP0 OUT data */
+static uint8_t *ao_usb_ep0_out_data;
+static uint8_t         ao_usb_ep0_out_len;
+
+/*
+ * Objects allocated in special USB memory
+ */
+
+/* Buffer description tables */
+static union stm_usb_bdt       *ao_usb_bdt;
+/* USB address of end of allocated storage */
+static uint16_t        ao_usb_sram_addr;
+
+/* Pointer to ep0 tx/rx buffers in USB memory */
+static uint32_t        *ao_usb_ep0_tx_buffer;
+static uint32_t        *ao_usb_ep0_rx_buffer;
+
+/* Pointer to bulk data tx/rx buffers in USB memory */
+static uint32_t        *ao_usb_in_tx_buffer;
+static uint32_t        *ao_usb_out_rx_buffer;
+
+/* System ram shadow of USB buffer; writing individual bytes is
+ * too much of a pain (sigh) */
+static uint8_t ao_usb_tx_buffer[AO_USB_IN_SIZE];
+static uint8_t ao_usb_tx_count;
+
+static uint8_t ao_usb_rx_buffer[AO_USB_OUT_SIZE];
+static uint8_t ao_usb_rx_count, ao_usb_rx_pos;
+
+/*
+ * End point register indices
+ */
+
+#define AO_USB_CONTROL_EPR     0
+#define AO_USB_INT_EPR         1
+#define AO_USB_OUT_EPR         2
+#define AO_USB_IN_EPR          3
+
+/* Marks when we don't need to send an IN packet.
+ * This happens only when the last IN packet is not full,
+ * otherwise the host will expect to keep seeing packets.
+ * Send a zero-length packet as required
+ */
+static uint8_t ao_usb_in_flushed;
+
+/* Marks when we have delivered an IN packet to the hardware
+ * and it has not been received yet. ao_sleep on this address
+ * to wait for it to be delivered.
+ */
+static uint8_t ao_usb_in_pending;
+
+/* Marks when an OUT packet has been received by the hardware
+ * but not pulled to the shadow buffer.
+ */
+static uint8_t ao_usb_out_avail;
+uint8_t                ao_usb_running;
+static uint8_t ao_usb_configuration;
+
+#define AO_USB_EP0_GOT_RESET   1
+#define AO_USB_EP0_GOT_SETUP   2
+#define AO_USB_EP0_GOT_RX_DATA 4
+#define AO_USB_EP0_GOT_TX_ACK  8
+
+static uint8_t ao_usb_ep0_receive;
+static uint8_t ao_usb_address;
+static uint8_t ao_usb_address_pending;
+
+static inline uint32_t set_toggle(uint32_t     current_value,
+                                  uint32_t     mask,
+                                  uint32_t     desired_value)
+{
+       return (current_value ^ desired_value) & mask;
+}
+
+static inline uint32_t *ao_usb_packet_buffer_addr(uint16_t sram_addr)
+{
+       return (uint32_t *) (((void *) ((uint8_t *) stm_usb_sram + 2 * sram_addr)));
+}
+
+static inline uint32_t ao_usb_epr_stat_rx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_STAT_RX) & STM_USB_EPR_STAT_RX_MASK;
+}
+
+static inline uint32_t ao_usb_epr_stat_tx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_STAT_TX) & STM_USB_EPR_STAT_TX_MASK;
+}
+
+static inline uint32_t ao_usb_epr_ctr_rx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_CTR_RX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_ctr_tx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_CTR_TX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_setup(uint32_t epr) {
+       return (epr >> STM_USB_EPR_SETUP) & 1;
+}
+
+static inline uint32_t ao_usb_epr_dtog_rx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_DTOG_RX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_dtog_tx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_DTOG_TX) & 1;
+}
+
+/*
+ * Set current device address and mark the
+ * interface as active
+ */
+static void
+ao_usb_set_address(uint8_t address)
+{
+       debug("ao_usb_set_address %02x\n", address);
+       stm_usb.daddr = (1 << STM_USB_DADDR_EF) | address;
+       ao_usb_address_pending = 0;
+}
+
+/*
+ * Write these values to preserve register contents under HW changes
+ */
+
+#define STM_USB_EPR_INVARIANT  ((1 << STM_USB_EPR_CTR_RX) |            \
+                                (STM_USB_EPR_DTOG_RX_WRITE_INVARIANT << STM_USB_EPR_DTOG_RX) | \
+                                (STM_USB_EPR_STAT_RX_WRITE_INVARIANT << STM_USB_EPR_STAT_RX) | \
+                                (1 << STM_USB_EPR_CTR_TX) |            \
+                                (STM_USB_EPR_DTOG_TX_WRITE_INVARIANT << STM_USB_EPR_DTOG_TX) | \
+                                (STM_USB_EPR_STAT_TX_WRITE_INVARIANT << STM_USB_EPR_STAT_TX))
+
+#define STM_USB_EPR_INVARIANT_MASK     ((1 << STM_USB_EPR_CTR_RX) |    \
+                                        (STM_USB_EPR_DTOG_RX_MASK << STM_USB_EPR_DTOG_RX) | \
+                                        (STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX) | \
+                                        (1 << STM_USB_EPR_CTR_TX) |    \
+                                        (STM_USB_EPR_DTOG_TX_MASK << STM_USB_EPR_DTOG_TX) | \
+                                        (STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX))
+
+/*
+ * These bits are purely under sw control, so preserve them in the
+ * register by re-writing what was read
+ */
+#define STM_USB_EPR_PRESERVE_MASK      ((STM_USB_EPR_EP_TYPE_MASK << STM_USB_EPR_EP_TYPE) | \
+                                        (1 << STM_USB_EPR_EP_KIND) |   \
+                                        (STM_USB_EPR_EA_MASK << STM_USB_EPR_EA))
+
+#define TX_DBG 0
+#define RX_DBG 0
+
+#if TX_DBG
+#define _tx_dbg0(msg) _dbg(__LINE__,msg,0)
+#define _tx_dbg1(msg,value) _dbg(__LINE__,msg,value)
+#else
+#define _tx_dbg0(msg)
+#define _tx_dbg1(msg,value)
+#endif
+
+#if RX_DBG
+#define _rx_dbg0(msg) _dbg(__LINE__,msg,0)
+#define _rx_dbg1(msg,value) _dbg(__LINE__,msg,value)
+#else
+#define _rx_dbg0(msg)
+#define _rx_dbg1(msg,value)
+#endif
+
+#if TX_DBG || RX_DBG
+static void _dbg(int line, char *msg, uint32_t value);
+#endif
+
+/*
+ * Set the state of the specified endpoint register to a new
+ * value. This is tricky because the bits toggle where the new
+ * value is one, and we need to write invariant values in other
+ * spots of the register. This hardware is strange...
+ */
+static void
+_ao_usb_set_stat_tx(int ep, uint32_t stat_tx)
+{
+       uint32_t        epr_write, epr_old;
+
+       _tx_dbg1("set_stat_tx top", stat_tx);
+       epr_old = epr_write = stm_usb.epr[ep];
+       epr_write &= STM_USB_EPR_PRESERVE_MASK;
+       epr_write |= STM_USB_EPR_INVARIANT;
+       epr_write |= set_toggle(epr_old,
+                             STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX,
+                             stat_tx << STM_USB_EPR_STAT_TX);
+       stm_usb.epr[ep] = epr_write;
+       _tx_dbg1("set_stat_tx bottom", epr_write);
+}
+
+static void
+ao_usb_set_stat_tx(int ep, uint32_t stat_tx)
+{
+       ao_arch_block_interrupts();
+       _ao_usb_set_stat_tx(ep, stat_tx);
+       ao_arch_release_interrupts();
+}
+
+static void
+_ao_usb_set_stat_rx(int ep, uint32_t stat_rx) {
+       uint32_t        epr_write, epr_old;
+
+       epr_write = epr_old = stm_usb.epr[ep];
+       epr_write &= STM_USB_EPR_PRESERVE_MASK;
+       epr_write |= STM_USB_EPR_INVARIANT;
+       epr_write |= set_toggle(epr_old,
+                             STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX,
+                             stat_rx << STM_USB_EPR_STAT_RX);
+       stm_usb.epr[ep] = epr_write;
+}
+
+static void
+ao_usb_set_stat_rx(int ep, uint32_t stat_rx) {
+       ao_arch_block_interrupts();
+       _ao_usb_set_stat_rx(ep, stat_rx);
+       ao_arch_release_interrupts();
+}
+
+/*
+ * Set just endpoint 0, for use during startup
+ */
+
+static void
+ao_usb_init_ep(uint8_t ep, uint32_t addr, uint32_t type, uint32_t stat_rx, uint32_t stat_tx)
+{
+       uint32_t                epr;
+       ao_arch_block_interrupts();
+       epr = stm_usb.epr[ep];
+       epr = ((0 << STM_USB_EPR_CTR_RX) |
+              (epr & (1 << STM_USB_EPR_DTOG_RX)) |
+              set_toggle(epr,
+                         (STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX),
+                         (stat_rx << STM_USB_EPR_STAT_RX)) |
+              (type << STM_USB_EPR_EP_TYPE) |
+              (0 << STM_USB_EPR_EP_KIND) |
+              (0 << STM_USB_EPR_CTR_TX) |
+              (epr & (1 << STM_USB_EPR_DTOG_TX)) |
+              set_toggle(epr,
+                         (STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX),
+                         (stat_tx << STM_USB_EPR_STAT_TX)) |
+              (addr << STM_USB_EPR_EA));
+       stm_usb.epr[ep] = epr;
+       ao_arch_release_interrupts();
+       debug ("writing epr[%d] 0x%08x wrote 0x%08x\n",
+              ep, epr, stm_usb.epr[ep]);
+}
+
+static void
+ao_usb_set_ep0(void)
+{
+       uint8_t e;
+
+       ao_usb_sram_addr = 0;
+
+       /* buffer table is at the start of USB memory */
+       stm_usb.btable = 0;
+       ao_usb_bdt = (void *) stm_usb_sram;
+
+       ao_usb_sram_addr += 8 * STM_USB_BDT_SIZE;
+
+       /* Set up EP 0 - a Control end point with 32 bytes of in and out buffers */
+
+       ao_usb_bdt[0].single.addr_tx = ao_usb_sram_addr;
+       ao_usb_bdt[0].single.count_tx = 0;
+       ao_usb_ep0_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_CONTROL_SIZE;
+
+       ao_usb_bdt[0].single.addr_rx = ao_usb_sram_addr;
+       ao_usb_bdt[0].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
+                                 (((AO_USB_CONTROL_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
+       ao_usb_ep0_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_CONTROL_SIZE;
+
+       ao_usb_init_ep(AO_USB_CONTROL_EPR, AO_USB_CONTROL_EP,
+                      STM_USB_EPR_EP_TYPE_CONTROL,
+                      STM_USB_EPR_STAT_RX_VALID,
+                      STM_USB_EPR_STAT_TX_NAK);
+
+       /* Clear all of the other endpoints */
+       for (e = 1; e < 8; e++) {
+               ao_usb_init_ep(e, 0,
+                              STM_USB_EPR_EP_TYPE_CONTROL,
+                              STM_USB_EPR_STAT_RX_DISABLED,
+                              STM_USB_EPR_STAT_TX_DISABLED);
+       }
+
+       ao_usb_set_address(0);
+
+       ao_usb_running = 0;
+
+       /* Reset our internal state
+        */
+
+       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+       ao_usb_ep0_in_data = NULL;
+       ao_usb_ep0_in_len = 0;
+
+       ao_usb_ep0_out_data = 0;
+       ao_usb_ep0_out_len = 0;
+}
+
+static void
+ao_usb_set_configuration(void)
+{
+       debug ("ao_usb_set_configuration\n");
+
+       /* Set up the INT end point */
+       ao_usb_bdt[AO_USB_INT_EPR].single.addr_tx = ao_usb_sram_addr;
+       ao_usb_bdt[AO_USB_INT_EPR].single.count_tx = 0;
+       ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_INT_SIZE;
+
+       ao_usb_init_ep(AO_USB_INT_EPR,
+                      AO_USB_INT_EP,
+                      STM_USB_EPR_EP_TYPE_INTERRUPT,
+                      STM_USB_EPR_STAT_RX_DISABLED,
+                      STM_USB_EPR_STAT_TX_NAK);
+
+       /* Set up the OUT end point */
+       ao_usb_bdt[AO_USB_OUT_EPR].single.addr_rx = ao_usb_sram_addr;
+       ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
+                                                     (((AO_USB_OUT_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
+       ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_OUT_SIZE;
+
+       ao_usb_init_ep(AO_USB_OUT_EPR,
+                      AO_USB_OUT_EP,
+                      STM_USB_EPR_EP_TYPE_BULK,
+                      STM_USB_EPR_STAT_RX_VALID,
+                      STM_USB_EPR_STAT_TX_DISABLED);
+
+       /* Set up the IN end point */
+       ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_sram_addr;
+       ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = 0;
+       ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_IN_SIZE;
+
+       ao_usb_init_ep(AO_USB_IN_EPR,
+                      AO_USB_IN_EP,
+                      STM_USB_EPR_EP_TYPE_BULK,
+                      STM_USB_EPR_STAT_RX_DISABLED,
+                      STM_USB_EPR_STAT_TX_NAK);
+
+       ao_usb_in_flushed = 0;
+       ao_usb_in_pending = 0;
+       ao_wakeup(&ao_usb_in_pending);
+
+       ao_usb_out_avail = 0;
+       ao_usb_configuration = 0;
+
+       ao_usb_running = 1;
+       ao_wakeup(&ao_usb_running);
+}
+
+static uint16_t        control_count;
+static uint16_t int_count;
+static uint16_t        in_count;
+static uint16_t        out_count;
+static uint16_t        reset_count;
+
+/* The USB memory holds 16 bit values on 32 bit boundaries
+ * and must be accessed only in 32 bit units. Sigh.
+ */
+
+static inline void
+ao_usb_write_byte(uint8_t byte, uint32_t *base, uint16_t offset)
+{
+       base += offset >> 1;
+       if (offset & 1) {
+               *base = (*base & 0xff) | ((uint32_t) byte << 8);
+       } else {
+               *base = (*base & 0xff00) | byte;
+       }
+}
+
+static inline void
+ao_usb_write_short(uint16_t data, uint32_t *base, uint16_t offset)
+{
+       base[offset>>1] = data;
+}
+
+static void
+ao_usb_write(const uint8_t *src, uint32_t *base, uint16_t bytes)
+{
+       uint16_t offset = 0;
+       if (!bytes)
+               return;
+       while (bytes >= 2) {
+               debug_data (" %02x %02x", src[0], src[1]);
+               ao_usb_write_short((uint16_t) ((uint16_t) (src[1] << 8) | (uint16_t) src[0]), base, offset);
+               offset += 2;
+               src += 2;
+               bytes -= 2;
+       }
+       if (bytes) {
+               debug_data (" %02x", src[0]);
+               ao_usb_write_byte(*src, base, offset);
+       }
+}
+
+static inline uint8_t
+ao_usb_read_byte(uint32_t *base, uint16_t offset)
+{
+       base += offset >> 1;
+       if (offset & 1)
+               return (*base >> 8) & 0xff;
+       else
+               return *base & 0xff;
+}
+
+static inline uint16_t
+ao_usb_read_short(uint32_t *base, uint16_t offset)
+{
+       return (uint16_t) (base[offset>>1]);
+}
+
+static void
+ao_usb_read(uint8_t *dst, uint32_t *base, uint16_t offset, uint16_t bytes)
+{
+       if (!bytes)
+               return;
+       if (offset & 1) {
+               *dst++ = ao_usb_read_byte(base, offset++);
+               debug_data (" %02x", dst[-1]);
+               bytes--;
+       }
+       while (bytes >= 2) {
+               uint16_t        s = ao_usb_read_short(base, offset);
+               dst[0] = (uint8_t) s;
+               dst[1] = (uint8_t) (s >> 8);
+               debug_data (" %02x %02x", dst[0], dst[1]);
+               offset += 2;
+               dst += 2;
+               bytes -= 2;
+       }
+       if (bytes) {
+               *dst = ao_usb_read_byte(base, offset);
+               debug_data (" %02x", dst[0]);
+       }
+}
+
+/* Send an IN data packet */
+static void
+ao_usb_ep0_flush(void)
+{
+       uint8_t this_len;
+
+       /* Check to see if the endpoint is still busy */
+       if (ao_usb_epr_stat_tx(stm_usb.epr[0]) == STM_USB_EPR_STAT_TX_VALID) {
+               debug("EP0 not accepting IN data\n");
+               return;
+       }
+
+       this_len = ao_usb_ep0_in_len;
+       if (this_len > AO_USB_CONTROL_SIZE)
+               this_len = AO_USB_CONTROL_SIZE;
+
+       if (this_len < AO_USB_CONTROL_SIZE)
+               ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+       ao_usb_ep0_in_len -= this_len;
+
+       debug_data ("Flush EP0 len %d:", this_len);
+       ao_usb_write(ao_usb_ep0_in_data, ao_usb_ep0_tx_buffer, this_len);
+       debug_data ("\n");
+       ao_usb_ep0_in_data += this_len;
+
+       /* Mark the endpoint as TX valid to send the packet */
+       ao_usb_bdt[AO_USB_CONTROL_EPR].single.count_tx = this_len;
+       ao_usb_set_stat_tx(AO_USB_CONTROL_EPR, STM_USB_EPR_STAT_TX_VALID);
+       debug ("queue tx. epr 0 now %08x\n", stm_usb.epr[AO_USB_CONTROL_EPR]);
+}
+
+/* Read data from the ep0 OUT fifo */
+static void
+ao_usb_ep0_fill(void)
+{
+       uint16_t        len = ao_usb_bdt[0].single.count_rx & STM_USB_BDT_COUNT_RX_COUNT_RX_MASK;
+
+       if (len > ao_usb_ep0_out_len)
+               len = ao_usb_ep0_out_len;
+       ao_usb_ep0_out_len -= (uint8_t) len;
+
+       /* Pull all of the data out of the packet */
+       debug_data ("Fill EP0 len %d:", len);
+       ao_usb_read(ao_usb_ep0_out_data, ao_usb_ep0_rx_buffer, 0, len);
+       debug_data ("\n");
+       ao_usb_ep0_out_data += len;
+
+       /* ACK the packet */
+       ao_usb_set_stat_rx(0, STM_USB_EPR_STAT_RX_VALID);
+}
+
+static void
+ao_usb_ep0_in_reset(void)
+{
+       ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
+       ao_usb_ep0_in_len = 0;
+}
+
+static void
+ao_usb_ep0_in_queue_byte(uint8_t a)
+{
+       if (ao_usb_ep0_in_len < sizeof (ao_usb_ep0_in_buf))
+               ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
+}
+
+static void
+ao_usb_ep0_in_set(const uint8_t *data, uint8_t len)
+{
+       ao_usb_ep0_in_data = data;
+       ao_usb_ep0_in_len = len;
+}
+
+static void
+ao_usb_ep0_out_set(uint8_t *data, uint8_t len)
+{
+       ao_usb_ep0_out_data = data;
+       ao_usb_ep0_out_len = len;
+}
+
+static void
+ao_usb_ep0_in_start(uint16_t max)
+{
+       /* Don't send more than asked for */
+       if (ao_usb_ep0_in_len > max)
+               ao_usb_ep0_in_len = (uint8_t) max;
+       ao_usb_ep0_flush();
+}
+
+struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
+
+/* Walk through the list of descriptors and find a match
+ */
+static void
+ao_usb_get_descriptor(uint16_t value, uint16_t length)
+{
+       const uint8_t           *descriptor;
+       uint8_t         type = (uint8_t) (value >> 8);
+       uint8_t         index = (uint8_t) value;
+
+       descriptor = ao_usb_descriptors;
+       while (descriptor[0] != 0) {
+               if (descriptor[1] == type && index-- == 0) {
+                       uint8_t len;
+                       if (type == AO_USB_DESC_CONFIGURATION)
+                               len = descriptor[2];
+                       else
+                               len = descriptor[0];
+                       if (len > length)
+                               len = (uint8_t) length;
+                       ao_usb_ep0_in_set(descriptor, len);
+                       break;
+               }
+               descriptor += descriptor[0];
+       }
+}
+
+static void
+ao_usb_ep0_setup(void)
+{
+       /* Pull the setup packet out of the fifo */
+       ao_usb_ep0_out_set((uint8_t *) &ao_usb_setup, 8);
+       ao_usb_ep0_fill();
+       if (ao_usb_ep0_out_len != 0) {
+               debug ("invalid setup packet length\n");
+               return;
+       }
+
+       if ((ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) || ao_usb_setup.length == 0)
+               ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+       else
+               ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
+
+       ao_usb_ep0_in_reset();
+
+       switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
+       case AO_USB_TYPE_STANDARD:
+               debug ("Standard setup packet\n");
+               switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
+               case AO_USB_RECIP_DEVICE:
+                       debug ("Device setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               debug ("get status\n");
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_ADDRESS:
+                               debug ("set address %d\n", ao_usb_setup.value);
+                               ao_usb_address = (uint8_t) ao_usb_setup.value;
+                               ao_usb_address_pending = 1;
+                               break;
+                       case AO_USB_REQ_GET_DESCRIPTOR:
+                               debug ("get descriptor %d\n", ao_usb_setup.value);
+                               ao_usb_get_descriptor(ao_usb_setup.value, ao_usb_setup.length);
+                               break;
+                       case AO_USB_REQ_GET_CONFIGURATION:
+                               debug ("get configuration %d\n", ao_usb_configuration);
+                               ao_usb_ep0_in_queue_byte(ao_usb_configuration);
+                               break;
+                       case AO_USB_REQ_SET_CONFIGURATION:
+                               ao_usb_configuration = (uint8_t) ao_usb_setup.value;
+                               debug ("set configuration %d\n", ao_usb_configuration);
+                               ao_usb_set_configuration();
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_INTERFACE:
+                       debug ("Interface setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_GET_INTERFACE:
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_INTERFACE:
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_ENDPOINT:
+                       debug ("Endpoint setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       }
+                       break;
+               }
+               break;
+       case AO_USB_TYPE_CLASS:
+               debug ("Class setup packet\n");
+               switch (ao_usb_setup.request) {
+               case AO_USB_SET_LINE_CODING:
+                       debug ("set line coding\n");
+                       ao_usb_ep0_out_set((uint8_t *) &ao_usb_line_coding, 7);
+                       break;
+               case AO_USB_GET_LINE_CODING:
+                       debug ("get line coding\n");
+                       ao_usb_ep0_in_set((const uint8_t *) &ao_usb_line_coding, 7);
+                       break;
+               case AO_USB_SET_CONTROL_LINE_STATE:
+                       break;
+               }
+               break;
+       }
+
+       /* If we're not waiting to receive data from the host,
+        * queue an IN response
+        */
+       if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+               ao_usb_ep0_in_start(ao_usb_setup.length);
+}
+
+static void
+ao_usb_ep0_handle(uint8_t receive)
+{
+       ao_usb_ep0_receive = 0;
+       if (receive & AO_USB_EP0_GOT_RESET) {
+               debug ("\treset\n");
+               ao_usb_set_ep0();
+               return;
+       }
+       if (receive & AO_USB_EP0_GOT_SETUP) {
+               debug ("\tsetup\n");
+               ao_usb_ep0_setup();
+       }
+       if (receive & AO_USB_EP0_GOT_RX_DATA) {
+               debug ("\tgot rx data\n");
+               if (ao_usb_ep0_state == AO_USB_EP0_DATA_OUT) {
+                       ao_usb_ep0_fill();
+                       if (ao_usb_ep0_out_len == 0) {
+                               ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+                               ao_usb_ep0_in_start(0);
+                       }
+               }
+       }
+       if (receive & AO_USB_EP0_GOT_TX_ACK) {
+               debug ("\tgot tx ack\n");
+
+#if HAS_FLIGHT && AO_USB_FORCE_IDLE
+               ao_flight_force_idle = 1;
+#endif
+               /* Wait until the IN packet is received from addr 0
+                * before assigning our local address
+                */
+               if (ao_usb_address_pending)
+                       ao_usb_set_address(ao_usb_address);
+               if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+                       ao_usb_ep0_flush();
+       }
+}
+
+void
+stm_usb_lp_isr(void)
+{
+       uint32_t        istr = stm_usb.istr;
+
+       if (istr & (1 << STM_USB_ISTR_CTR)) {
+               uint8_t         ep = istr & STM_USB_ISTR_EP_ID_MASK;
+               uint32_t        epr, epr_write;
+
+               /* Preserve the SW write bits, don't mess with most HW writable bits,
+                * clear the CTR_RX and CTR_TX bits
+                */
+               epr = stm_usb.epr[ep];
+               epr_write = epr;
+               epr_write &= STM_USB_EPR_PRESERVE_MASK;
+               epr_write |= STM_USB_EPR_INVARIANT;
+               epr_write &= ~(1UL << STM_USB_EPR_CTR_RX);
+               epr_write &= ~(1UL << STM_USB_EPR_CTR_TX);
+               stm_usb.epr[ep] = epr_write;
+
+               switch (ep) {
+               case 0:
+                       ++control_count;
+                       if (ao_usb_epr_ctr_rx(epr)) {
+                               if (ao_usb_epr_setup(epr))
+                                       ao_usb_ep0_receive |= AO_USB_EP0_GOT_SETUP;
+                               else
+                                       ao_usb_ep0_receive |= AO_USB_EP0_GOT_RX_DATA;
+                       }
+                       if (ao_usb_epr_ctr_tx(epr))
+                               ao_usb_ep0_receive |= AO_USB_EP0_GOT_TX_ACK;
+                       ao_usb_ep0_handle(ao_usb_ep0_receive);
+                       break;
+               case AO_USB_OUT_EPR:
+                       ++out_count;
+                       if (ao_usb_epr_ctr_rx(epr)) {
+                               _rx_dbg1("RX ISR", epr);
+                               ao_usb_out_avail = 1;
+                               _rx_dbg0("out avail set");
+                               ao_wakeup(AO_USB_OUT_SLEEP_ADDR);
+                               _rx_dbg0("stdin awoken");
+                       }
+                       break;
+               case AO_USB_IN_EPR:
+                       ++in_count;
+                       _tx_dbg1("TX ISR", epr);
+                       if (ao_usb_epr_ctr_tx(epr)) {
+                               ao_usb_in_pending = 0;
+                               ao_wakeup(&ao_usb_in_pending);
+                       }
+                       break;
+               case AO_USB_INT_EPR:
+                       ++int_count;
+                       if (ao_usb_epr_ctr_tx(epr))
+                               _ao_usb_set_stat_tx(AO_USB_INT_EPR, STM_USB_EPR_STAT_TX_NAK);
+                       break;
+               }
+               return;
+       }
+
+       if (istr & (1 << STM_USB_ISTR_RESET)) {
+               ++reset_count;
+               stm_usb.istr &= ~(1UL << STM_USB_ISTR_RESET);
+               ao_usb_ep0_receive |= AO_USB_EP0_GOT_RESET;
+               ao_usb_ep0_handle(ao_usb_ep0_receive);
+       }
+}
+
+void
+stm_usb_wakeup_isr(void)
+{
+       /* USB wakeup, just clear the bit for now */
+       stm_usb.istr &= ~(1UL << STM_USB_ISTR_WKUP);
+}
+
+/* Queue the current IN buffer for transmission */
+static void
+_ao_usb_in_send(void)
+{
+       _tx_dbg0("in_send start");
+       debug ("send %d\n", ao_usb_tx_count);
+       while (ao_usb_in_pending)
+               ao_sleep(&ao_usb_in_pending);
+       ao_usb_in_pending = 1;
+       if (ao_usb_tx_count != AO_USB_IN_SIZE)
+               ao_usb_in_flushed = 1;
+       ao_usb_write(ao_usb_tx_buffer, ao_usb_in_tx_buffer, ao_usb_tx_count);
+       ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = ao_usb_tx_count;
+       ao_usb_tx_count = 0;
+       _ao_usb_set_stat_tx(AO_USB_IN_EPR, STM_USB_EPR_STAT_TX_VALID);
+       _tx_dbg0("in_send end");
+}
+
+/* Wait for a free IN buffer. Interrupts are blocked */
+static void
+_ao_usb_in_wait(void)
+{
+       for (;;) {
+               /* Check if the current buffer is writable */
+               if (ao_usb_tx_count < AO_USB_IN_SIZE)
+                       break;
+
+               _tx_dbg0("in_wait top");
+               /* Wait for an IN buffer to be ready */
+               while (ao_usb_in_pending)
+                       ao_sleep(&ao_usb_in_pending);
+               _tx_dbg0("in_wait bottom");
+       }
+}
+
+void
+ao_usb_flush(void)
+{
+       if (!ao_usb_running)
+               return;
+
+       /* Anytime we've sent a character since
+        * the last time we flushed, we'll need
+        * to send a packet -- the only other time
+        * we would send a packet is when that
+        * packet was full, in which case we now
+        * want to send an empty packet
+        */
+       ao_arch_block_interrupts();
+       while (!ao_usb_in_flushed) {
+               _tx_dbg0("flush top");
+               _ao_usb_in_send();
+               _tx_dbg0("flush end");
+       }
+       ao_arch_release_interrupts();
+}
+
+void
+ao_usb_putchar(char c)
+{
+       if (!ao_usb_running)
+               return;
+
+       ao_arch_block_interrupts();
+       _ao_usb_in_wait();
+
+       ao_usb_in_flushed = 0;
+       ao_usb_tx_buffer[ao_usb_tx_count++] = (uint8_t) c;
+
+       /* Send the packet when full */
+       if (ao_usb_tx_count == AO_USB_IN_SIZE) {
+               _tx_dbg0("putchar full");
+               _ao_usb_in_send();
+               _tx_dbg0("putchar flushed");
+       }
+       ao_arch_release_interrupts();
+}
+
+static void
+_ao_usb_out_recv(void)
+{
+       _rx_dbg0("out_recv top");
+       ao_usb_out_avail = 0;
+
+       ao_usb_rx_count = (uint8_t) (ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx & STM_USB_BDT_COUNT_RX_COUNT_RX_MASK);
+
+       _rx_dbg1("out_recv count", ao_usb_rx_count);
+       debug ("recv %d\n", ao_usb_rx_count);
+       debug_data("Fill OUT len %d:", ao_usb_rx_count);
+       ao_usb_read(ao_usb_rx_buffer, ao_usb_out_rx_buffer, 0, ao_usb_rx_count);
+       debug_data("\n");
+       ao_usb_rx_pos = 0;
+
+       /* ACK the packet */
+       _ao_usb_set_stat_rx(AO_USB_OUT_EPR, STM_USB_EPR_STAT_RX_VALID);
+}
+
+static int
+_ao_usb_pollchar(void)
+{
+       uint8_t c;
+
+       if (!ao_usb_running)
+               return AO_READ_AGAIN;
+
+       for (;;) {
+               if (ao_usb_rx_pos != ao_usb_rx_count)
+                       break;
+
+               _rx_dbg0("poll check");
+               /* Check to see if a packet has arrived */
+               if (!ao_usb_out_avail) {
+                       _rx_dbg0("poll none");
+                       return AO_READ_AGAIN;
+               }
+               _ao_usb_out_recv();
+       }
+
+       /* Pull a character out of the fifo */
+       c = ao_usb_rx_buffer[ao_usb_rx_pos++];
+       return c;
+}
+
+char
+ao_usb_getchar(void)
+{
+       int     c;
+
+       ao_arch_block_interrupts();
+       while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN)
+               ao_sleep(AO_USB_OUT_SLEEP_ADDR);
+       ao_arch_release_interrupts();
+       return (char) c;
+}
+
+#ifndef HAS_USB_DISABLE
+#define HAS_USB_DISABLE 1
+#endif
+
+#if HAS_USB_DISABLE
+void
+ao_usb_disable(void)
+{
+       ao_arch_block_interrupts();
+       stm_usb.cntr = (1 << STM_USB_CNTR_FRES);
+       stm_usb.istr = 0;
+
+#if HAS_USB_PULLUP
+       /* Disable USB pull-up */
+       ao_gpio_set(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, 0);
+#endif
+
+       /* Switch off the device */
+       stm_usb.cntr = (1 << STM_USB_CNTR_PDWN) | (1 << STM_USB_CNTR_FRES);
+
+       /* Disable the interface */
+       stm_rcc.apb1enr &= ~(1UL << STM_RCC_APB1ENR_USBEN);
+       ao_arch_release_interrupts();
+}
+#endif
+
+void
+ao_usb_enable(void)
+{
+       int     t;
+
+#if HAS_USB_PULLUP
+       ao_gpio_set(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, 0);
+#endif
+
+       /* Enable USB device */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USBEN);
+
+       /* Do not touch the GPIOA configuration; USB takes priority
+        * over GPIO on pins A11 and A12, but if you select alternate
+        * input 10 (the documented correct selection), then USB is
+        * pulled low and doesn't work at all
+        */
+
+       ao_arch_block_interrupts();
+
+       /* Route interrupts */
+       stm_nvic_set_priority(STM_ISR_USB_LP_POS, AO_STM_NVIC_LOW_PRIORITY);
+       stm_nvic_set_enable(STM_ISR_USB_LP_POS);
+
+       ao_usb_configuration = 0;
+
+       stm_usb.cntr = (1 << STM_USB_CNTR_FRES);
+
+       /* Clear the power down bit */
+       stm_usb.cntr = 0;
+
+       /* Clear any spurious interrupts */
+       stm_usb.istr = 0;
+
+       debug ("ao_usb_enable\n");
+
+       /* Enable interrupts */
+       stm_usb.cntr = ((1 << STM_USB_CNTR_CTRM) |
+                       (0 << STM_USB_CNTR_PMAOVRM) |
+                       (0 << STM_USB_CNTR_ERRM) |
+                       (0 << STM_USB_CNTR_WKUPM) |
+                       (0 << STM_USB_CNTR_SUSPM) |
+                       (1 << STM_USB_CNTR_RESETM) |
+                       (0 << STM_USB_CNTR_SOFM) |
+                       (0 << STM_USB_CNTR_ESOFM) |
+                       (0 << STM_USB_CNTR_RESUME) |
+                       (0 << STM_USB_CNTR_FSUSP) |
+                       (0 << STM_USB_CNTR_LP_MODE) |
+                       (0 << STM_USB_CNTR_PDWN) |
+                       (0 << STM_USB_CNTR_FRES));
+
+       ao_arch_release_interrupts();
+
+       for (t = 0; t < 1000; t++)
+               ao_arch_nop();
+
+       /* Enable USB pull-up */
+#if HAS_USB_PULLUP
+       ao_gpio_set(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, 1);
+#endif
+}
+
+#if USB_ECHO
+struct ao_task ao_usb_echo_task;
+
+static void
+ao_usb_echo(void)
+{
+       char    c;
+
+       for (;;) {
+               c = ao_usb_getchar();
+               ao_usb_putchar(c);
+               ao_usb_flush();
+       }
+}
+#endif
+
+#if USB_DEBUG
+static void
+ao_usb_irq(void)
+{
+       printf ("control: %d out: %d in: %d int: %d reset: %d\n",
+               control_count, out_count, in_count, int_count, reset_count);
+}
+
+const struct ao_cmds ao_usb_cmds[] = {
+       { ao_usb_irq, "I\0Show USB interrupt counts" },
+       { 0, NULL }
+};
+#endif
+
+void
+ao_usb_init(void)
+{
+#if HAS_USB_PULLUP
+       int     i;
+       ao_enable_output(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, 0);
+       for (i = 0; i < 40000; i++)
+               ao_arch_nop();
+#endif
+       ao_usb_enable();
+
+       debug ("ao_usb_init\n");
+       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+#if USB_ECHO
+       ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo");
+#endif
+#if USB_DEBUG
+       ao_cmd_register(&ao_usb_cmds[0]);
+#endif
+#if !USB_ECHO
+#if USE_USB_STDIO
+       ao_add_stdio(_ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
+#endif
+#endif
+}
+
+#if TX_DBG || RX_DBG
+
+struct ao_usb_dbg {
+       int             line;
+       char            *msg;
+       uint32_t        value;
+       uint32_t        prival;
+#if TX_DBG
+       uint16_t        in_count;
+       uint32_t        in_epr;
+       uint32_t        in_pending;
+       uint32_t        tx_count;
+       uint32_t        in_flushed;
+#endif
+#if RX_DBG
+       uint8_t         rx_count;
+       uint8_t         rx_pos;
+       uint8_t         out_avail;
+       uint32_t        out_epr;
+#endif
+};
+
+#define NUM_USB_DBG    16
+
+static struct ao_usb_dbg dbg[NUM_USB_DBG];
+static int dbg_i;
+
+static void _dbg(int line, char *msg, uint32_t value)
+{
+       uint32_t        prival;
+       dbg[dbg_i].line = line;
+       dbg[dbg_i].msg = msg;
+       dbg[dbg_i].value = value;
+#if AO_NONMASK_INTERRUPT
+       asm("mrs %0,basepri" : "=&r" (prival));
+#else
+       asm("mrs %0,primask" : "=&r" (prival));
+#endif
+       dbg[dbg_i].prival = prival;
+#if TX_DBG
+       dbg[dbg_i].in_count = in_count;
+       dbg[dbg_i].in_epr = stm_usb.epr[AO_USB_IN_EPR];
+       dbg[dbg_i].in_pending = ao_usb_in_pending;
+       dbg[dbg_i].tx_count = ao_usb_tx_count;
+       dbg[dbg_i].in_flushed = ao_usb_in_flushed;
+#endif
+#if RX_DBG
+       dbg[dbg_i].rx_count = ao_usb_rx_count;
+       dbg[dbg_i].rx_pos = ao_usb_rx_pos;
+       dbg[dbg_i].out_avail = ao_usb_out_avail;
+       dbg[dbg_i].out_epr = stm_usb.epr[AO_USB_OUT_EPR];
+#endif
+       if (++dbg_i == NUM_USB_DBG)
+               dbg_i = 0;
+}
+#endif
diff --git a/src/stm32f1/openocd-stm32f1 b/src/stm32f1/openocd-stm32f1
new file mode 100755 (executable)
index 0000000..20b074e
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+#OPENOCD=openocd
+#OPENOCD=/usr/bin/openocd
+#OPENOCD=/local/src/openocd/src/openocd
+OPENOCD=/local/bin/openocd
+exec $OPENOCD \
+       -f interface/stlink.cfg \
+       -c 'transport select hla_swd' \
+       -f target/stm32f1x.cfg -c init \
+       -c 'reset halt' \
+       -c 'stm32f1x.cpu arm semihosting enable' \
+       "$@"
diff --git a/src/stm32f1/registers.ld b/src/stm32f1/registers.ld
new file mode 100644 (file)
index 0000000..abaebc8
--- /dev/null
@@ -0,0 +1,45 @@
+stm_crc           = 0x40023000;
+stm_flash  = 0x40022000;
+stm_rcc    = 0x40021000;
+stm_dma    = 0x40020000;
+stm_usart1 = 0x40013800;
+stm_spi1   = 0x40013000;
+stm_tim1   = 0x40012c00;
+stm_adc2   = 0x40012800;
+stm_adc1   = 0x40012400;
+stm_gpioe  = 0x40011800;
+stm_gpiod  = 0x40011400;
+stm_gpioc  = 0x40011000;
+stm_gpiob  = 0x40010c00;
+stm_gpioa  = 0x40010800;
+stm_exti   = 0x40010400;
+stm_afio   = 0x40010000;
+stm_pwr    = 0x40007000;
+stm_bkp    = 0x40006c00;
+stm_bxcan  = 0x40006400;
+stm_usb_sram = 0x40006000;
+stm_usb    = 0x40005c00;
+stm_i2c2   = 0x40005800;
+stm_i2c1   = 0x40005400;
+stm_usart3 = 0x40004800;
+stm_usart2 = 0x40004400;
+stm_spi2   = 0x40003800;
+stm_iwdg   = 0x40003000;
+stm_wwdg   = 0x40002c00;
+stm_rtc    = 0x40002800;
+stm_tim4   = 0x40000800;
+stm_tim3   = 0x40000400;
+stm_tim2   = 0x40000000;
+
+stm_systick = 0xe000e010;
+
+stm_nvic   = 0xe000e100;
+
+stm_scb    = 0xe000ed00;
+
+stm_mpu    = 0xe000ed90;
+
+stm_dbgmcu = 0xe0042000;
+
+/* data in system memory */
+stm_flash_data = 0x1ffff7e0;
diff --git a/src/stm32f1/stm32f1.h b/src/stm32f1/stm32f1.h
new file mode 100644 (file)
index 0000000..664e765
--- /dev/null
@@ -0,0 +1,1960 @@
+/*
+ * Copyright © 2023 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _STM32F1_H_
+#define _STM32F1_H_
+
+#include <stdint.h>
+
+typedef volatile uint32_t      vuint32_t;
+typedef volatile uint16_t      vuint16_t;
+typedef volatile void *                vvoid_t;
+
+struct stm_rcc {
+       vuint32_t       cr;
+       vuint32_t       cfgr;
+       vuint32_t       cir;
+       vuint32_t       apb2rstr;
+
+       vuint32_t       apb1rstr;
+       vuint32_t       ahbenr;
+       vuint32_t       apb2enr;
+       vuint32_t       apb1enr;
+
+       vuint32_t       bdcr;
+       vuint32_t       csr;
+       vuint32_t       ahbstr;
+       vuint32_t       cfgr2;
+};
+
+extern struct stm_rcc stm_rcc;
+
+//#define stm_rcc    (*((struct stm_rcc *) 0x40021000))
+
+#define STM_RCC_CR_RTCPRE      (29)
+#define  STM_RCC_CR_RTCPRE_HSE_DIV_2   0
+#define  STM_RCC_CR_RTCPRE_HSE_DIV_4   1
+#define  STM_RCC_CR_RTCPRE_HSE_DIV_8   2
+#define  STM_RCC_CR_RTCPRE_HSE_DIV_16  3
+#define  STM_RCC_CR_RTCPRE_HSE_MASK    3UL
+
+#define STM_RCC_CR_PLL3RDY     (29)
+#define STM_RCC_CR_PLL3ON      (28)
+#define STM_RCC_CR_PLL2RDY     (27)
+#define STM_RCC_CR_PLL2ON      (26)
+#define STM_RCC_CR_PLLRDY      (25)
+#define STM_RCC_CR_PLLON       (24)
+#define STM_RCC_CR_CSSON       (19)
+#define STM_RCC_CR_HSEBYP      (18)
+#define STM_RCC_CR_HSERDY      (17)
+#define STM_RCC_CR_HSEON       (16)
+#define STM_RCC_CR_HSICAL      (8)
+#define STM_RCC_CR_HSITRIM     (3)
+#define STM_RCC_CR_HSIRDY      (1)
+#define STM_RCC_CR_HSION       (0)
+
+#define STM_RCC_CFGR_MCO       (24)
+#define  STM_RCC_CFGR_MCO_DISABLE      0
+#define  STM_RCC_CFGR_MCO_SYSCLK       4
+#define  STM_RCC_CFGR_MCO_HSI  5
+#define  STM_RCC_CFGR_MCO_HSE  6
+#define  STM_RCC_CFGR_MCO_PLL_2        7
+#define  STM_RCC_CFGR_MCO_MASK 7UL
+
+#define STM_RCC_CFGR_USBPRE    (22)
+#define  STM_RCC_CFGR_USBPRE_1_5       0
+#define  STM_RCC_CFGR_USBPRE_1         1
+
+#define STM_RCC_CFGR_PLLMUL    (18)
+#define  STM_RCC_CFGR_PLLMUL_2         0
+#define  STM_RCC_CFGR_PLLMUL_3         1
+#define  STM_RCC_CFGR_PLLMUL_4         2
+#define  STM_RCC_CFGR_PLLMUL_5         3
+#define  STM_RCC_CFGR_PLLMUL_6         4
+#define  STM_RCC_CFGR_PLLMUL_7         5
+#define  STM_RCC_CFGR_PLLMUL_8         6
+#define  STM_RCC_CFGR_PLLMUL_9         7
+#define  STM_RCC_CFGR_PLLMUL_10                8
+#define  STM_RCC_CFGR_PLLMUL_11                9
+#define  STM_RCC_CFGR_PLLMUL_12                10
+#define  STM_RCC_CFGR_PLLMUL_13                11
+#define  STM_RCC_CFGR_PLLMUL_14                12
+#define  STM_RCC_CFGR_PLLMUL_15                13
+#define  STM_RCC_CFGR_PLLMUL_16                14
+#define  STM_RCC_CFGR_PLLMUL_MASK      0xfUL
+
+#define STM_RCC_CFGR_PLLXTPRE  (17)
+#define  STM_RCC_CFGR_PLLXTPRE_1       0
+#define  STM_RCC_CFGR_PLLXTPRE_2       1
+#define  STM_RCC_CFGR_PLLXTPRE_MASK    1UL
+
+#define STM_RCC_CFGR_PLLSRC    (16)
+#define  STM_RCC_CFGR_PLLSRC_HSI_2     0
+#define  STM_RCC_CFGR_PLLSRC_HSE       1
+
+#define STM_RCC_CFGR_ADCPRE    (14)
+#define  STM_RCC_CFGR_ADCPRE_2         0
+#define  STM_RCC_CFGR_ADCPRE_4         1
+#define  STM_RCC_CFGR_ADCPRE_6         2
+#define  STM_RCC_CFGR_ADCPRE_8         3
+#define  STM_RCC_CFGR_ADCPRE_MASK      3UL
+
+#define STM_RCC_CFGR_PPRE2     (11)
+#define  STM_RCC_CFGR_PPRE2_DIV_1      0
+#define  STM_RCC_CFGR_PPRE2_DIV_2      4
+#define  STM_RCC_CFGR_PPRE2_DIV_4      5
+#define  STM_RCC_CFGR_PPRE2_DIV_8      6
+#define  STM_RCC_CFGR_PPRE2_DIV_16     7
+#define  STM_RCC_CFGR_PPRE2_MASK       7UL
+
+#define STM_RCC_CFGR_PPRE1     (8)
+#define  STM_RCC_CFGR_PPRE1_DIV_1      0
+#define  STM_RCC_CFGR_PPRE1_DIV_2      4
+#define  STM_RCC_CFGR_PPRE1_DIV_4      5
+#define  STM_RCC_CFGR_PPRE1_DIV_8      6
+#define  STM_RCC_CFGR_PPRE1_DIV_16     7
+#define  STM_RCC_CFGR_PPRE1_MASK       7UL
+
+#define STM_RCC_CFGR_HPRE      (4)
+#define  STM_RCC_CFGR_HPRE_DIV_1       0
+#define  STM_RCC_CFGR_HPRE_DIV_2       8
+#define  STM_RCC_CFGR_HPRE_DIV_4       9
+#define  STM_RCC_CFGR_HPRE_DIV_8       0xa
+#define  STM_RCC_CFGR_HPRE_DIV_16      0xb
+#define  STM_RCC_CFGR_HPRE_DIV_64      0xc
+#define  STM_RCC_CFGR_HPRE_DIV_128     0xd
+#define  STM_RCC_CFGR_HPRE_DIV_256     0xe
+#define  STM_RCC_CFGR_HPRE_DIV_512     0xf
+#define  STM_RCC_CFGR_HPRE_MASK                0xfUL
+
+#define STM_RCC_CFGR_SWS       (2)
+#define  STM_RCC_CFGR_SWS_HSI          0
+#define  STM_RCC_CFGR_SWS_HSE          1
+#define  STM_RCC_CFGR_SWS_PLL          2
+#define  STM_RCC_CFGR_SWS_MASK         3UL
+
+#define STM_RCC_CFGR_SW                (0)
+#define  STM_RCC_CFGR_SW_HSI           0
+#define  STM_RCC_CFGR_SW_HSE           1
+#define  STM_RCC_CFGR_SW_PLL           2
+#define  STM_RCC_CFGR_SW_MASK          3UL
+
+#define STM_RCC_AHBENR_CRCEN   6
+#define STM_RCC_AHBENR_FLITFEN 4
+#define STM_RCC_AHBENR_SRAMEN  2
+#define STM_RCC_AHBENR_DMA2EN  1
+#define STM_RCC_AHBENR_DMA1EN  0
+
+
+#define STM_RCC_APB2ENR_USART1EN       14
+#define STM_RCC_APB2ENR_SPI1EN         12
+#define STM_RCC_APB2ENR_TIM1EN         11
+#define STM_RCC_APB2ENR_ADC2EN         10
+#define STM_RCC_APB2ENR_ADC1EN         9
+#define STM_RCC_APB2ENR_IOPEEN         6
+#define STM_RCC_APB2ENR_IOPDEN         5
+#define STM_RCC_APB2ENR_IOPCEN         4
+#define STM_RCC_APB2ENR_IOPBEN         3
+#define STM_RCC_APB2ENR_IOPAEN         2
+#define STM_RCC_APB2ENR_AFIOEN         0
+
+#define STM_RCC_APB1ENR_DACEN          29
+#define STM_RCC_APB1ENR_PWREN          28
+#define STM_RCC_APB1ENR_BKPEN          27
+#define STM_RCC_APB1ENR_CANEN          26
+#define STM_RCC_APB1ENR_USBEN          23
+#define STM_RCC_APB1ENR_I2C2EN         22
+#define STM_RCC_APB1ENR_I2C1EN         21
+#define STM_RCC_APB1ENR_UART5EN                20
+#define STM_RCC_APB1ENR_UART4EN                19
+#define STM_RCC_APB1ENR_USART3EN       18
+#define STM_RCC_APB1ENR_USART2EN       17
+#define STM_RCC_APB1ENR_SPI3EN         15
+#define STM_RCC_APB1ENR_SPI2EN         14
+#define STM_RCC_APB1ENR_WWDGEN         11
+#define STM_RCC_APB1ENR_TIM7EN         5
+#define STM_RCC_APB1ENR_TIM6EN         4
+#define STM_RCC_APB1ENR_TIM5EN         3
+#define STM_RCC_APB1ENR_TIM4EN         2
+#define STM_RCC_APB1ENR_TIM3EN         1
+#define STM_RCC_APB1ENR_TIM2EN         0
+
+#define STM_RCC_CSR_LPWRRSTF           (31)
+#define STM_RCC_CSR_WWDGRSTF           (30)
+#define STM_RCC_CSR_IWDGRSTF           (29)
+#define STM_RCC_CSR_SFTRSTF            (28)
+#define STM_RCC_CSR_PORRSTF            (27)
+#define STM_RCC_CSR_PINRSTF            (26)
+#define STM_RCC_CSR_RMVF               (24)
+#define STM_RCC_CSR_LSIRDY             (1)
+#define STM_RCC_CSR_LSION              (0)
+
+struct stm_systick {
+       vuint32_t       ctrl;
+       vuint32_t       load;
+       vuint32_t       val;
+       vuint32_t       calib;
+};
+
+extern struct stm_systick stm_systick;
+
+//#define stm_systick  (*((struct stm_systick *) 0xe000e010))
+
+#define STM_SYSTICK_CTRL_ENABLE                0
+#define STM_SYSTICK_CTRL_TICKINT       1
+#define STM_SYSTICK_CTRL_CLKSOURCE     2
+#define  STM_SYSTICK_CTRL_CLKSOURCE_HCLK_8             0
+#define  STM_SYSTICK_CTRL_CLKSOURCE_HCLK               1
+#define STM_SYSTICK_CTRL_COUNTFLAG     16
+
+/* The NVIC starts at 0xe000e100, so add that to the offsets to find the absolute address */
+
+struct stm_nvic {
+       vuint32_t       iser[3];        /* 0x000 0xe000e100 Set Enable Register */
+
+       uint8_t         _unused00c[0x080 - 0x00c];
+
+       vuint32_t       icer[3];        /* 0x080 0xe000e180 Clear Enable Register */
+
+       uint8_t         _unused08c[0x100 - 0x08c];
+
+       vuint32_t       ispr[3];        /* 0x100 0xe000e200 Set Pending Register */
+
+       uint8_t         _unused10c[0x180 - 0x10c];
+
+       vuint32_t       icpr[3];        /* 0x180 0xe000e280 Clear Pending Register */
+
+       uint8_t         _unused18c[0x200 - 0x18c];
+
+       vuint32_t       iabr[3];        /* 0x200 0xe000e300 Active Bit Register */
+
+       uint8_t         _unused20c[0x300 - 0x20c];
+
+       vuint32_t       ipr[31];        /* 0x300 0xe000e400 Priority Register */
+
+       uint8_t         _unused37c[0xe00 - 0x37c];      /* covers SCB */
+
+       vuint32_t       stir;           /* 0xe00 0xe000ee00 Software Trigger Interrupt Register */
+};
+
+extern struct stm_nvic stm_nvic;
+
+//#define stm_nvic (*((struct stm_nvic *) 0xe000e100))
+
+#define IRQ_REG(irq)   ((irq) >> 5)
+#define IRQ_BIT(irq)   ((irq) & 0x1f)
+#define IRQ_MASK(irq)  (1 << IRQ_BIT(irq))
+#define IRQ_BOOL(v,irq)        (((v) >> IRQ_BIT(irq)) & 1)
+
+static inline void
+stm_nvic_set_enable(int irq) {
+       stm_nvic.iser[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline void
+stm_nvic_clear_enable(int irq) {
+       stm_nvic.icer[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline int
+stm_nvic_enabled(int irq) {
+       return IRQ_BOOL(stm_nvic.iser[IRQ_REG(irq)], irq);
+}
+
+static inline void
+stm_nvic_set_pending(int irq) {
+       stm_nvic.ispr[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline void
+stm_nvic_clear_pending(int irq) {
+       stm_nvic.icpr[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline int
+stm_nvic_pending(int irq) {
+       return IRQ_BOOL(stm_nvic.ispr[IRQ_REG(irq)], irq);
+}
+
+static inline int
+stm_nvic_active(int irq) {
+       return IRQ_BOOL(stm_nvic.iabr[IRQ_REG(irq)], irq);
+}
+
+#define IRQ_PRIO_REG(irq)      ((irq) >> 2)
+#define IRQ_PRIO_BIT(irq)      (((irq) & 3) << 3)
+#define IRQ_PRIO_MASK(irq)     (0xff << IRQ_PRIO_BIT(irq))
+
+static inline void
+stm_nvic_set_priority(int irq, uint8_t prio) {
+       int             n = IRQ_PRIO_REG(irq);
+       uint32_t        v;
+
+       v = stm_nvic.ipr[n];
+       v &= (uint32_t) ~IRQ_PRIO_MASK(irq);
+       v |= (prio) << IRQ_PRIO_BIT(irq);
+       stm_nvic.ipr[n] = v;
+}
+
+static inline uint8_t
+stm_nvic_get_priority(int irq) {
+       return (stm_nvic.ipr[IRQ_PRIO_REG(irq)] >> IRQ_PRIO_BIT(irq)) & IRQ_PRIO_MASK(0);
+}
+
+struct stm_scb {
+       vuint32_t       cpuid;
+       vuint32_t       icsr;
+       vuint32_t       vtor;
+       vuint32_t       aircr;
+
+       vuint32_t       scr;
+       vuint32_t       ccr;
+       vuint32_t       shpr1;
+       vuint32_t       shpr2;
+
+       vuint32_t       shpr3;
+       vuint32_t       shcrs;
+       vuint32_t       cfsr;
+       vuint32_t       hfsr;
+
+       uint32_t        unused_30;
+       vuint32_t       mmar;
+       vuint32_t       bfar;
+};
+
+extern struct stm_scb stm_scb;
+
+#define STM_SCB_AIRCR_VECTKEY          16
+#define  STM_SCB_AIRCR_VECTKEY_KEY             0x05fa
+#define STM_SCB_AIRCR_PRIGROUP         8
+#define STM_SCB_AIRCR_SYSRESETREQ      2
+#define STM_SCB_AIRCR_VECTCLRACTIVE    1
+#define STM_SCB_AIRCR_VECTRESET                0
+
+struct stm_dbgmcu {
+       uint32_t        idcode;
+};
+
+extern struct stm_dbgmcu       stm_dbgmcu;
+
+static inline uint16_t
+stm_dev_id(void) {
+       return stm_dbgmcu.idcode & 0xfff;
+}
+
+struct stm_flash {
+       vuint32_t       acr;
+       vuint32_t       keyr;
+       vuint32_t       optkeyr;
+       vuint32_t       sr;
+
+       vuint32_t       cr;
+       vuint32_t       ar;
+       uint32_t        _unused018;
+       vuint32_t       obr;
+
+       vuint32_t       wrpr;
+};
+
+extern struct stm_flash stm_flash;
+
+//#define stm_flash (*((struct stm_flash *) 0x40022000))
+
+#define STM_FLASH_ACR_PRFTBS   5
+#define STM_FLASH_ACR_PRFTBE   4
+#define STM_FLASH_ACR_HLFCYA   3
+#define STM_FLASH_ACR_LATENCY  0
+#define  STM_FLASH_ACR_LATENCY_0       0
+#define  STM_FLASH_ACR_LATENCY_1       1
+#define  STM_FLASH_ACR_LATENCY_2       2
+
+#define STM_FLASH_SR_EOP       5
+#define STM_FLASH_SR_WRPRTERR  4
+#define STM_FLASH_SR_PGERR     2
+#define STM_FLASH_SR_BSY       0
+
+#define STM_FLASH_CR_EOPIE     12
+#define STM_FLASH_CR_ERRIE     10
+#define STM_FLASH_CR_OPTWRE    9
+#define STM_FLASH_CR_LOCK      7
+#define STM_FLASH_CR_STRT      6
+#define STM_FLASH_CR_OPTER     5
+#define STM_FLASH_CR_OPTPG     4
+#define STM_FLASH_CR_MER       2
+#define STM_FLASH_CR_PER       1
+#define STM_FLASH_CR_PG                0
+
+#define STM_FLASH_RDPRT_KEY    0x00A5
+#define STM_FLASH_KEYR_KEY1    0x45670123
+#define STM_FLASH_KEYR_KEY2    0xCDEF89AB
+
+
+struct stm_flash_data {
+       vuint16_t       f_size;
+       vuint16_t       unused02;
+       vuint32_t       unused04;
+       vuint32_t       device_id[3];
+};
+
+extern struct stm_flash_data   stm_flash_data;
+
+static inline uint32_t stm_flash_size(void) { return (uint32_t) stm_flash_data.f_size * 1024; }
+
+//#define stm_flash_data       (*((struct stm_flash_data *) 0x1ffff7e0))
+
+struct stm_gpio {
+       vuint32_t       cr[2];
+       vuint32_t       idr;
+       vuint32_t       odr;
+
+       vuint32_t       bsrr;
+       vuint32_t       brr;
+       vuint32_t       lckr;
+};
+
+#define STM_GPIO_CR(y)         ((uint8_t) (y) >> 3)
+#define STM_GPIO_CR_CNF(y)     ((((uint8_t) (y) & 7) << 2) + 2)
+#define  STM_GPIO_CR_CNF_INPUT_ANALOG          0
+#define  STM_GPIO_CR_CNF_INPUT_FLOATING                1
+#define  STM_GPIO_CR_CNF_INPUT_PULL            2
+#define  STM_GPIO_CR_CNF_OUTPUT_PUSH_PULL      0
+#define         STM_GPIO_CR_CNF_OUTPUT_OPEN_DRAIN      1
+#define         STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL    2
+#define         STM_GPIO_CR_CNF_OUTPUT_AF_OPEN_DRAIN   3
+#define  STM_GPIO_CR_CNF_MASK                  3U
+#define STM_GPIO_CR_MODE(y)    ((((y) & 7) << 2))
+#define  STM_GPIO_CR_MODE_INPUT                        0
+#define  STM_GPIO_CR_MODE_OUTPUT_10MHZ         1
+#define  STM_GPIO_CR_MODE_OUTPUT_2MHZ          2
+#define  STM_GPIO_CR_MODE_OUTPUT_50MHZ         3
+#define  STM_GPIO_CR_MODE_MASK                 3U
+
+static inline void
+stm_gpio_conf(struct stm_gpio *gpio, int pin, uint8_t mode, uint8_t cnf)
+{
+       uint8_t         cr = STM_GPIO_CR(pin);
+       uint32_t        v = gpio->cr[cr];
+
+       v &= ~((STM_GPIO_CR_CNF_MASK << STM_GPIO_CR_CNF(pin)) |
+              (STM_GPIO_CR_MODE_MASK << STM_GPIO_CR_MODE(pin)));
+       v |= (mode << STM_GPIO_CR_MODE(pin)) | (cnf << STM_GPIO_CR_CNF(pin));
+       gpio->cr[cr] = v;
+}
+
+static inline void
+stm_gpio_set(struct stm_gpio *gpio, int pin, uint8_t value) {
+       /* Use the bit set/reset register to do this atomically */
+       gpio->bsrr = ((uint32_t) (value ^ 1) << (pin + 16)) | ((uint32_t) value << pin);
+}
+
+static inline void
+stm_gpio_set_mask(struct stm_gpio *gpio, uint16_t bits, uint16_t mask) {
+       /* Use the bit set/reset register to do this atomically */
+       gpio->bsrr = ((uint32_t) (~bits & mask) << 16) | ((uint32_t) (bits & mask));
+}
+
+static inline void
+stm_gpio_set_bits(struct stm_gpio *gpio, uint16_t bits) {
+       gpio->bsrr = bits;
+}
+
+static inline void
+stm_gpio_clr_bits(struct stm_gpio *gpio, uint16_t bits) {
+       gpio->bsrr = ((uint32_t) bits) << 16;
+}
+
+static inline uint8_t
+stm_gpio_get(struct stm_gpio *gpio, int pin) {
+       return (gpio->idr >> pin) & 1;
+}
+
+static inline uint16_t
+stm_gpio_get_all(struct stm_gpio *gpio) {
+       return (uint16_t) gpio->idr;
+}
+
+extern struct stm_gpio stm_gpioa;
+extern struct stm_gpio stm_gpiob;
+extern struct stm_gpio stm_gpioc;
+extern struct stm_gpio stm_gpiod;
+extern struct stm_gpio stm_gpioe;
+
+#define stm_gpioe  (*((struct stm_gpio *) 0x40011800))
+#define stm_gpiod  (*((struct stm_gpio *) 0x40011400))
+#define stm_gpioc  (*((struct stm_gpio *) 0x40011000))
+#define stm_gpiob  (*((struct stm_gpio *) 0x40010c00))
+#define stm_gpioa  (*((struct stm_gpio *) 0x40010800))
+
+struct stm_afio {
+       vuint32_t       evcr;
+       vuint32_t       mapr;
+       vuint32_t       exticr[4];
+       vuint32_t       mapr2;
+};
+
+extern struct stm_afio stm_afio;
+
+#define stm_afio       (*((struct stm_afio *) 0x40010000))
+
+#define STM_AFIO_MAPR_SWJ_CFG                  24
+#define  STM_AFIO_MAPR_SWJ_CFG_FULL_SWJ                        0
+#define  STM_AFIO_MAPR_SWJ_CFG_FULL_SWJ_NO_NJTRST      1
+#define  STM_AFIO_MAPR_SWJ_CFG_SW_DP                   2
+#define  STM_AFIO_MAPR_SWJ_CFG_DISABLE                 4
+#define  STM_AFIO_MAPR_SWJ_CFG_MASK                    7UL
+#define STM_AFIO_MAPR_ADC2_ETRGREG_REMAP       20
+#define STM_AFIO_MAPR_ADC2_ETRGINJ_REMAP       19
+#define STM_AFIO_MAPR_ADC1_ETRGREG_REMAP       18
+#define STM_AFIO_MAPR_ADC1_ETRGINJ_REMAP       17
+#define STM_AFIO_MAPR_TIM5CH4_IREMAP           16
+#define STM_AFIO_MAPR_PD01_REMAP               15
+#define STM_AFIO_MAPR_CAN_REMAP                        13
+#define  STM_AFIO_MAPR_CAN_REMAP_PA11_PA12             0
+#define  STM_AFIO_MAPR_CAN_REMAP_PB8_PB9               2
+#define  STM_AFIO_MAPR_CAN_REMAP_PD0_PD1               3
+#define  STM_AFIO_MAPR_CAN_REMAP_MASK                  3UL
+#define STM_AFIO_MAPR_TIM4_REMAP               12
+#define STM_AFIO_MAPR_TIM3_REMAP               10
+#define  STM_AFIO_MAPR_TIM3_REMAP_PA6_PA7_PB0_PB1      0
+#define  STM_AFIO_MAPR_TIM3_REMAP_PB4_PB5_PB0_PB1      2
+#define  STM_AFIO_MAPR_TIM3_REMAP_PC6_PC7_PC8_PC9      3
+#define  STM_AFIO_MAPR_TIM3_REMAP_MASK                 3UL
+#define STM_AFIO_MAPR_TIM2_REMAP               8
+#define  STM_AFIO_MAPR_TIM2_REMAP_PA0_PA1_PA2_PA3      0
+#define  STM_AFIO_MAPR_TIM2_REMAP_PA15_PB3_PA2_PA3     1
+#define  STM_AFIO_MAPR_TIM2_REMAP_PA0_PA1_PB10_PB11    2
+#define  STM_AFIO_MAPR_TIM2_REMAP_PA15_PB3_PB10_PB11   3
+#define  STM_AFIO_MAPR_TIM2_REMAP_MASK                 3UL
+#define STM_AFIO_MAPR_TIM1_REMAP               6
+#define  STM_AFIO_MAPR_TIM1_REMAP_PA12_PA8_PA9_PA10_PA11_PB12_PB13_PB14_PB15   0
+#define  STM_AFIO_MAPR_TIM1_REMAP_PA12_PA8_PA9_PA10_PA11_PA6_PA7_PB0_PB1       1
+#define  STM_AFIO_MAPR_TIM1_REMAP_PE7_PE9_PE11_PE13_PE14_PE15_PE8_PE10_PE12    3
+#define  STM_AFIO_MAPR_TIM1_REMAP_MASK                                         3
+#define STM_AFIO_MAPR_USART3_REMAP             4
+#define  STM_AFIO_MAPR_USART3_REMAP_PB10_PB11_PB12_PB13_PB14   0
+#define  STM_AFIO_MAPR_USART3_REMAP_PC10_PC11_PC12_PB13_PB14   1
+#define  STM_AFIO_MAPR_USART3_REMAP_PD8_PD9_PD10_PD11_PD12     3
+#define  STM_AFIO_MAPR_USART3_REMAP_MASK                       3
+#define STM_AFIO_MAPR_USART2_REMAP             3
+#define  STM_AFIO_MAPR_USART2_REMAP_PA0_PA1_PA2_PA3_PA4        0
+#define  STM_AFIO_MAPR_USART2_REMAP_PD3_PD4_PD5_PD6_PD7        1
+#define  STM_AFIO_MAPR_USART2_REMAP_MASK               1
+#define STM_AFIO_MAPR_USART1_REMAP             2
+#define  STM_AFIO_MAPR_USART1_REMAP_PA9_PA10           0
+#define  STM_AFIO_MAPR_USART1_REMAP_PB6_PB7            1
+#define  STM_AFIO_MAPR_USART1_REMAP_MASK               1
+#define STM_AFIO_MAPR_I2C1_REMAP               1
+#define  STM_AFIO_MAPR_I2C1_REMAP_PB6_PB7              0
+#define  STM_AFIO_MAPR_I2C1_REMAP_PB8_PB9              1
+#define  STM_AFIO_MAPR_I2C1_REMAP_MASK                 1
+#define STM_AFIO_MAPR_SPI1_REMAP               0
+#define  STM_AFIO_MAPR_SPI1_REMAP_PA4_PA5_PA6_PA7      0
+#define  STM_AFIO_MAPR_SPI1_REMAP_PA15_PB3_PB4_PB5     1
+#define  STM_AFIO_MAPR_SPI1_REMAP_MASK                 1
+
+#define STM_AFIO_EXTICR_PA             0
+#define STM_AFIO_EXTICR_PB             1
+#define STM_AFIO_EXTICR_PC             2
+#define STM_AFIO_EXTICR_PD             3
+#define STM_AFIO_EXTICR_PE             4
+#define STM_AFIO_EXTICR_PF             5
+#define STM_AFIO_EXTICR_PG             6
+
+static inline void
+stm_set_afio_mapr(uint8_t bit, uint32_t val, uint32_t mask) {
+       uint32_t        mapr = stm_afio.mapr;
+
+       mapr &= ~(mask << bit);
+       mapr |= (val << bit);
+       stm_afio.mapr = mapr;
+}
+
+struct stm_usart {
+       vuint32_t       sr;     /* status register */
+       vuint32_t       dr;     /* data register */
+       vuint32_t       brr;    /* baud rate register */
+       vuint32_t       cr1;    /* control register 1 */
+
+       vuint32_t       cr2;    /* control register 2 */
+       vuint32_t       cr3;    /* control register 3 */
+       vuint32_t       gtpr;   /* guard time and prescaler */
+};
+
+extern struct stm_usart stm_usart1;
+extern struct stm_usart stm_usart2;
+extern struct stm_usart stm_usart3;
+
+//#define stm_usart1   (*((struct stm_usart *) 0x40013800))
+//#define stm_usart2   (*((struct stm_usart *) 0x40004800))
+//#define stm_usart3   (*((struct stm_usart *) 0x40004400))
+
+#define STM_USART_SR_CTS       (9)     /* CTS flag */
+#define STM_USART_SR_LBD       (8)     /* LIN break detection flag */
+#define STM_USART_SR_TXE       (7)     /* Transmit data register empty */
+#define STM_USART_SR_TC                (6)     /* Transmission complete */
+#define STM_USART_SR_RXNE      (5)     /* Read data register not empty */
+#define STM_USART_SR_IDLE      (4)     /* IDLE line detected */
+#define STM_USART_SR_ORE       (3)     /* Overrun error */
+#define STM_USART_SR_NE                (2)     /* Noise detected flag */
+#define STM_USART_SR_FE                (1)     /* Framing error */
+#define STM_USART_SR_PE                (0)     /* Parity error */
+
+#define STM_USART_BRR_DIV_MANTISSA     (4)
+#define STM_USART_BRR_DIV_FRACTION     (0)
+
+#define STM_USART_CR1_UE       (13)    /* USART enable */
+#define STM_USART_CR1_M                (12)    /* Word length */
+#define STM_USART_CR1_WAKE     (11)    /* Wakeup method */
+#define STM_USART_CR1_PCE      (10)    /* Parity control enable */
+#define STM_USART_CR1_PS       (9)     /* Parity selection */
+#define STM_USART_CR1_PEIE     (8)     /* PE interrupt enable */
+#define STM_USART_CR1_TXEIE    (7)     /* TXE interrupt enable */
+#define STM_USART_CR1_TCIE     (6)     /* Transmission complete interrupt enable */
+#define STM_USART_CR1_RXNEIE   (5)     /* RXNE interrupt enable */
+#define STM_USART_CR1_IDLEIE   (4)     /* IDLE interrupt enable */
+#define STM_USART_CR1_TE       (3)     /* Transmitter enable */
+#define STM_USART_CR1_RE       (2)     /* Receiver enable */
+#define STM_USART_CR1_RWU      (1)     /* Receiver wakeup */
+#define STM_USART_CR1_SBK      (0)     /* Send break */
+
+#define STM_USART_CR2_LINEN    (14)    /* LIN mode enable */
+#define STM_USART_CR2_STOP     (12)    /* STOP bits */
+#define STM_USART_CR2_STOP_MASK        3UL
+#define STM_USART_CR2_STOP_1   0
+#define STM_USART_CR2_STOP_0_5 1
+#define STM_USART_CR2_STOP_2   2
+#define STM_USART_CR2_STOP_1_5 3
+
+#define STM_USART_CR2_CLKEN    (11)    /* Clock enable */
+#define STM_USART_CR2_CPOL     (10)    /* Clock polarity */
+#define STM_USART_CR2_CPHA     (9)     /* Clock phase */
+#define STM_USART_CR2_LBCL     (8)     /* Last bit clock pulse */
+#define STM_USART_CR2_LBDIE    (6)     /* LIN break detection interrupt enable */
+#define STM_USART_CR2_LBDL     (5)     /* lin break detection length */
+#define STM_USART_CR2_ADD      (0)
+#define STM_USART_CR2_ADD_MASK 0xfUL
+
+#define STM_USART_CR3_CTSIE    (10)    /* CTS interrupt enable */
+#define STM_USART_CR3_CTSE     (9)     /* CTS enable */
+#define STM_USART_CR3_RTSE     (8)     /* RTS enable */
+#define STM_USART_CR3_DMAT     (7)     /* DMA enable transmitter */
+#define STM_USART_CR3_DMAR     (6)     /* DMA enable receiver */
+#define STM_USART_CR3_SCEN     (5)     /* Smartcard mode enable */
+#define STM_USART_CR3_NACK     (4)     /* Smartcard NACK enable */
+#define STM_USART_CR3_HDSEL    (3)     /* Half-duplex selection */
+#define STM_USART_CR3_IRLP     (2)     /* IrDA low-power */
+#define STM_USART_CR3_IREN     (1)     /* IrDA mode enable */
+#define STM_USART_CR3_EIE      (0)     /* Error interrupt enable */
+
+struct stm_usb {
+       vuint32_t       epr[8];
+       uint8_t         reserved_20[0x40 - 0x20];
+       vuint32_t       cntr;
+       vuint32_t       istr;
+       vuint32_t       fnr;
+       vuint32_t       daddr;
+       vuint32_t       btable;
+};
+
+/*
+ * USB DM: PA11
+ * USB DP: PA12
+ *
+ * Need a pull-up on a separate GPIO
+ */
+#define STM_USB_EPR_CTR_RX     15
+#define  STM_USB_EPR_CTR_RX_WRITE_INVARIANT            1
+#define STM_USB_EPR_DTOG_RX    14
+#define STM_USB_EPR_DTOG_RX_WRITE_INVARIANT            0
+#define STM_USB_EPR_STAT_RX    12
+#define  STM_USB_EPR_STAT_RX_DISABLED                  0
+#define  STM_USB_EPR_STAT_RX_STALL                     1
+#define  STM_USB_EPR_STAT_RX_NAK                       2
+#define  STM_USB_EPR_STAT_RX_VALID                     3
+#define  STM_USB_EPR_STAT_RX_MASK                      3UL
+#define  STM_USB_EPR_STAT_RX_WRITE_INVARIANT           0
+#define STM_USB_EPR_SETUP      11
+#define STM_USB_EPR_EP_TYPE    9
+#define  STM_USB_EPR_EP_TYPE_BULK                      0
+#define  STM_USB_EPR_EP_TYPE_CONTROL                   1
+#define  STM_USB_EPR_EP_TYPE_ISO                       2
+#define  STM_USB_EPR_EP_TYPE_INTERRUPT                 3
+#define  STM_USB_EPR_EP_TYPE_MASK                      3UL
+#define STM_USB_EPR_EP_KIND    8
+#define  STM_USB_EPR_EP_KIND_DBL_BUF                   1       /* Bulk */
+#define  STM_USB_EPR_EP_KIND_STATUS_OUT                        1       /* Control */
+#define STM_USB_EPR_CTR_TX     7
+#define  STM_USB_CTR_TX_WRITE_INVARIANT                        1
+#define STM_USB_EPR_DTOG_TX    6
+#define  STM_USB_EPR_DTOG_TX_WRITE_INVARIANT           0
+#define STM_USB_EPR_STAT_TX    4
+#define  STM_USB_EPR_STAT_TX_DISABLED                  0
+#define  STM_USB_EPR_STAT_TX_STALL                     1
+#define  STM_USB_EPR_STAT_TX_NAK                       2
+#define  STM_USB_EPR_STAT_TX_VALID                     3
+#define  STM_USB_EPR_STAT_TX_WRITE_INVARIANT           0
+#define  STM_USB_EPR_STAT_TX_MASK                      3UL
+#define STM_USB_EPR_EA         0
+#define  STM_USB_EPR_EA_MASK                           0xfUL
+
+#define STM_USB_CNTR_CTRM      15
+#define STM_USB_CNTR_PMAOVRM   14
+#define STM_USB_CNTR_ERRM      13
+#define STM_USB_CNTR_WKUPM     12
+#define STM_USB_CNTR_SUSPM     11
+#define STM_USB_CNTR_RESETM    10
+#define STM_USB_CNTR_SOFM      9
+#define STM_USB_CNTR_ESOFM     8
+#define STM_USB_CNTR_RESUME    4
+#define STM_USB_CNTR_FSUSP     3
+#define STM_USB_CNTR_LP_MODE   2
+#define STM_USB_CNTR_PDWN      1
+#define STM_USB_CNTR_FRES      0
+
+#define STM_USB_ISTR_CTR       15
+#define STM_USB_ISTR_PMAOVR    14
+#define STM_USB_ISTR_ERR       13
+#define STM_USB_ISTR_WKUP      12
+#define STM_USB_ISTR_SUSP      11
+#define STM_USB_ISTR_RESET     10
+#define STM_USB_ISTR_SOF       9
+#define STM_USB_ISTR_ESOF      8
+#define STM_USB_ISTR_DIR       4
+#define STM_USB_ISTR_EP_ID     0
+#define  STM_USB_ISTR_EP_ID_MASK               0xfUL
+
+#define STM_USB_FNR_RXDP       15
+#define STM_USB_FNR_RXDM       14
+#define STM_USB_FNR_LCK                13
+#define STM_USB_FNR_LSOF       11
+#define  STM_USB_FNR_LSOF_MASK                 0x3UL
+#define STM_USB_FNR_FN         0
+#define  STM_USB_FNR_FN_MASK                   0x7ffUL
+
+#define STM_USB_DADDR_EF       7
+#define STM_USB_DADDR_ADD      0
+#define  STM_USB_DADDR_ADD_MASK                        0x7fUL
+
+extern struct stm_usb stm_usb;
+
+#define stm_usb (*((struct stm_usb *) 0x40005c00))
+
+union stm_usb_bdt {
+       struct {
+               vuint32_t       addr_tx;
+               vuint32_t       count_tx;
+               vuint32_t       addr_rx;
+               vuint32_t       count_rx;
+       } single;
+       struct {
+               vuint32_t       addr;
+               vuint32_t       count;
+       } double_tx[2];
+       struct {
+               vuint32_t       addr;
+               vuint32_t       count;
+       } double_rx[2];
+};
+
+#define STM_USB_BDT_COUNT_RX_BL_SIZE   15
+#define STM_USB_BDT_COUNT_RX_NUM_BLOCK 10
+#define  STM_USB_BDT_COUNT_RX_NUM_BLOCK_MASK   0x1fUL
+#define STM_USB_BDT_COUNT_RX_COUNT_RX  0
+#define  STM_USB_BDT_COUNT_RX_COUNT_RX_MASK    0x3ffUL
+
+#define STM_USB_BDT_SIZE       8
+
+extern uint8_t stm_usb_sram[] __attribute__ ((aligned(4)));
+
+//#define stm_usb_sram ((uint8_t *)0x40006000)
+
+struct stm_dma_channel {
+       vuint32_t       ccr;
+       vuint32_t       cndtr;
+       vvoid_t         cpar;
+       vvoid_t         cmar;
+       vuint32_t       reserved;
+};
+
+#define STM_NUM_DMA    7
+
+struct stm_dma {
+       vuint32_t               isr;
+       vuint32_t               ifcr;
+       struct stm_dma_channel  channel[STM_NUM_DMA];
+};
+
+extern struct stm_dma stm_dma;
+
+#define stm_dma        (*((struct stm_dma *) 0x40020000))
+
+/* DMA channels go from 1 to 7, instead of 0 to 6 (sigh)
+ */
+
+#define STM_DMA_INDEX(channel)         ((channel) - 1)
+
+#define STM_DMA_ISR(index)             ((index) << 2)
+#define STM_DMA_ISR_MASK                       0xfUL
+#define STM_DMA_ISR_TEIF                       3
+#define STM_DMA_ISR_HTIF                       2
+#define STM_DMA_ISR_TCIF                       1
+#define STM_DMA_ISR_GIF                                0
+
+#define STM_DMA_IFCR(index)            ((index) << 2)
+#define STM_DMA_IFCR_MASK                      0xfUL
+#define STM_DMA_IFCR_CTEIF                     3
+#define STM_DMA_IFCR_CHTIF                     2
+#define STM_DMA_IFCR_CTCIF                     1
+#define STM_DMA_IFCR_CGIF                      0
+
+#define STM_DMA_CCR_MEM2MEM            (14)
+
+#define STM_DMA_CCR_PL                 (12)
+#define  STM_DMA_CCR_PL_LOW                    (0)
+#define  STM_DMA_CCR_PL_MEDIUM                 (1)
+#define  STM_DMA_CCR_PL_HIGH                   (2)
+#define  STM_DMA_CCR_PL_VERY_HIGH              (3)
+#define  STM_DMA_CCR_PL_MASK                   (3)
+
+#define STM_DMA_CCR_MSIZE              (10)
+#define  STM_DMA_CCR_MSIZE_8                   (0)
+#define  STM_DMA_CCR_MSIZE_16                  (1)
+#define  STM_DMA_CCR_MSIZE_32                  (2)
+#define  STM_DMA_CCR_MSIZE_MASK                        (3)
+
+#define STM_DMA_CCR_PSIZE              (8)
+#define  STM_DMA_CCR_PSIZE_8                   (0)
+#define  STM_DMA_CCR_PSIZE_16                  (1)
+#define  STM_DMA_CCR_PSIZE_32                  (2)
+#define  STM_DMA_CCR_PSIZE_MASK                        (3)
+
+#define STM_DMA_CCR_MINC               (7)
+#define STM_DMA_CCR_PINC               (6)
+#define STM_DMA_CCR_CIRC               (5)
+#define STM_DMA_CCR_DIR                        (4)
+#define  STM_DMA_CCR_DIR_PER_TO_MEM            0
+#define  STM_DMA_CCR_DIR_MEM_TO_PER            1
+#define STM_DMA_CCR_TEIE               (3)
+#define STM_DMA_CCR_HTIE               (2)
+#define STM_DMA_CCR_TCIE               (1)
+#define STM_DMA_CCR_EN                 (0)
+
+#define STM_DMA_CHANNEL_ADC1           1
+#define STM_DMA_CHANNEL_SPI1_RX                2
+#define STM_DMA_CHANNEL_SPI1_TX                3
+#define STM_DMA_CHANNEL_SPI2_RX                4
+#define STM_DMA_CHANNEL_SPI2_TX                5
+#define STM_DMA_CHANNEL_USART3_TX      2
+#define STM_DMA_CHANNEL_USART3_RX      3
+#define STM_DMA_CHANNEL_USART1_TX      4
+#define STM_DMA_CHANNEL_USART1_RX      5
+#define STM_DMA_CHANNEL_USART2_RX      6
+#define STM_DMA_CHANNEL_USART2_TX      7
+#define STM_DMA_CHANNEL_I2C2_TX                4
+#define STM_DMA_CHANNEL_I2C2_RX                5
+#define STM_DMA_CHANNEL_I2C1_TX                6
+#define STM_DMA_CHANNEL_I2C1_RX                7
+#define STM_DMA_CHANNEL_TIM1_CH1       2
+#define STM_DMA_CHANNEL_TIM1_CH4       4
+#define STM_DMA_CHANNEL_TIM1_TRIG      4
+#define STM_DMA_CHANNEL_TIM1_COM       4
+#define STM_DMA_CHANNEL_TIM1_UP                5
+#define STM_DMA_CHANNEL_TIM1_CH3       6
+#define STM_DMA_CHANNEL_TIM2_CH3       1
+#define STM_DMA_CHANNEL_TIM2_UP                2
+#define STM_DMA_CHANNEL_TIM2_CH1       5
+#define STM_DMA_CHANNEL_TIM2_CH2       7
+#define STM_DMA_CHANNEL_TIM2_CH4       7
+#define STM_DMA_CHANNEL_TIM3_CH3       2
+#define STM_DMA_CHANNEL_TIM3_CH4       3
+#define STM_DMA_CHANNEL_TIM3_UP                3
+#define STM_DMA_CHANNEL_TIM3_CH1       6
+#define STM_DMA_CHANNEL_TIM3_TRIG      6
+#define STM_DMA_CHANNEL_TIM4_CH1       1
+#define STM_DMA_CHANNEL_TIM4_CH2       4
+#define STM_DMA_CHANNEL_TIM4_CH3       5
+#define STM_DMA_CHANNEL_TIM4_UP                7
+
+/* high density, xl-density and connectivity devices also have dma2 */
+
+#define STM_DMA2_CHANNEL_ADC3          5
+#define STM_DMA2_CHANNEL_SPI3_RX       1
+#define STM_DMA2_CHANNEL_SPI3_TX       2
+#define STM_DMA2_CHANNEL_UART4_RX      3
+#define STM_DMA2_CHANNEL_UART4_TX      5
+#define STM_DMA2_CHANNEL_TIM5_CH4      1
+#define STM_DMA2_CHANNEL_TIM5_TRIG     1
+#define STM_DMA2_CHANNEL_TIM5_CH3      2
+#define STM_DMA2_CHANNEL_TIM5_UP       2
+#define STM_DMA2_CHANNEL_TIM5_CH2      4
+#define STM_DMA2_CHANNEL_TIM5_CH1      5
+#define STM_DMA2_CHANNEL_TIM6_UP       3
+#define STM_DMA2_CHANNEL_DAC_CHANNEL1  3
+#define STM_DMA2_CHANNEL_TIM7_UP       4
+#define STM_DMA2_CHANNEL_DAC_CHANNEL2  4
+#define STM_DMA2_CHANNEL_TIM8_CH3      1
+#define STM_DMA2_CHANNEL_TIM8_UP       1
+#define STM_DMA2_CHANNEL_TIM8_CH4      2
+#define STM_DMA2_CHANNEL_TIM8_TRIG     2
+#define STM_DMA2_CHANNEL_TIM8_COM      2
+#define STM_DMA2_CHANNEL_TIM8_CH1      3
+#define STM_DMA2_CHANNEL_TIM8_CH2      5
+
+struct stm_spi {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       sr;
+       vuint32_t       dr;
+
+       vuint32_t       crcpr;
+       vuint32_t       rxcrcr;
+       vuint32_t       txcrcr;
+       vuint32_t       i2scfgr;
+
+       vuint32_t       i2spr;
+};
+
+extern struct stm_spi stm_spi1, stm_spi2;
+
+#define stm_spi1 (*((struct stm_spi *) 0x40013000))
+#define stm_spi2 (*((struct stm_spi *) 0x40003800))
+
+/* SPI channels go from 1 to 2, instead of 0 to 1 (sigh)
+ */
+
+#define STM_NUM_SPI    2
+
+#define STM_SPI_INDEX(channel)         ((channel) - 1)
+
+#define STM_SPI_CR1_BIDIMODE           15
+#define STM_SPI_CR1_BIDIOE             14
+#define STM_SPI_CR1_CRCEN              13
+#define STM_SPI_CR1_CRCNEXT            12
+#define STM_SPI_CR1_DFF                        11
+#define STM_SPI_CR1_RXONLY             10
+#define STM_SPI_CR1_SSM                        9
+#define STM_SPI_CR1_SSI                        8
+#define STM_SPI_CR1_LSBFIRST           7
+#define STM_SPI_CR1_SPE                        6
+#define STM_SPI_CR1_BR                 3
+#define  STM_SPI_CR1_BR_PCLK_2                 0
+#define  STM_SPI_CR1_BR_PCLK_4                 1
+#define  STM_SPI_CR1_BR_PCLK_8                 2
+#define  STM_SPI_CR1_BR_PCLK_16                        3
+#define  STM_SPI_CR1_BR_PCLK_32                        4
+#define  STM_SPI_CR1_BR_PCLK_64                        5
+#define  STM_SPI_CR1_BR_PCLK_128               6
+#define  STM_SPI_CR1_BR_PCLK_256               7
+#define  STM_SPI_CR1_BR_MASK                   7UL
+
+#define STM_SPI_CR1_MSTR               2
+#define STM_SPI_CR1_CPOL               1
+#define STM_SPI_CR1_CPHA               0
+
+#define STM_SPI_CR2_TXEIE      7
+#define STM_SPI_CR2_RXNEIE     6
+#define STM_SPI_CR2_ERRIE      5
+#define STM_SPI_CR2_SSOE       2
+#define STM_SPI_CR2_TXDMAEN    1
+#define STM_SPI_CR2_RXDMAEN    0
+
+#define STM_SPI_SR_FRE         8
+#define STM_SPI_SR_BSY         7
+#define STM_SPI_SR_OVR         6
+#define STM_SPI_SR_MODF                5
+#define STM_SPI_SR_CRCERR      4
+#define STM_SPI_SR_UDR         3
+#define STM_SPI_SR_CHSIDE      2
+#define STM_SPI_SR_TXE         1
+#define STM_SPI_SR_RXNE                0
+
+#define STM_NUM_I2C    2
+
+#define STM_I2C_INDEX(channel) ((channel) - 1)
+
+struct stm_i2c {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       oar1;
+       vuint32_t       oar2;
+       vuint32_t       dr;
+       vuint32_t       sr1;
+       vuint32_t       sr2;
+       vuint32_t       ccr;
+       vuint32_t       trise;
+};
+
+extern struct stm_i2c stm_i2c1, stm_i2c2;
+
+#define stm_i2c1       (*((struct stm_i2c *) 0x40005400))
+#define stm_i2c2       (*((struct stm_i2c *) 0x40005800))
+
+#define STM_I2C_CR1_SWRST      15
+#define STM_I2C_CR1_ALERT      13
+#define STM_I2C_CR1_PEC                12
+#define STM_I2C_CR1_POS                11
+#define STM_I2C_CR1_ACK                10
+#define STM_I2C_CR1_STOP       9
+#define STM_I2C_CR1_START      8
+#define STM_I2C_CR1_NOSTRETCH  7
+#define STM_I2C_CR1_ENGC       6
+#define STM_I2C_CR1_ENPEC      5
+#define STM_I2C_CR1_ENARP      4
+#define STM_I2C_CR1_SMBTYPE    3
+#define STM_I2C_CR1_SMBUS      1
+#define STM_I2C_CR1_PE         0
+
+#define STM_I2C_CR2_LAST       12
+#define STM_I2C_CR2_DMAEN      11
+#define STM_I2C_CR2_ITBUFEN    10
+#define STM_I2C_CR2_ITEVTEN    9
+#define STM_I2C_CR2_ITERREN    8
+#define STM_I2C_CR2_FREQ       0
+#define  STM_I2C_CR2_FREQ_MASK         0x3fUL
+
+#define STM_I2C_SR1_SMBALERT   15
+#define STM_I2C_SR1_TIMEOUT    14
+#define STM_I2C_SR1_PECERR     12
+#define STM_I2C_SR1_OVR                11
+#define STM_I2C_SR1_AF         10
+#define STM_I2C_SR1_ARLO       9
+#define STM_I2C_SR1_BERR       8
+#define STM_I2C_SR1_TXE                7
+#define STM_I2C_SR1_RXNE       6
+#define STM_I2C_SR1_STOPF      4
+#define STM_I2C_SR1_ADD10      3
+#define STM_I2C_SR1_BTF                2
+#define STM_I2C_SR1_ADDR       1
+#define STM_I2C_SR1_SB         0
+
+#define STM_I2C_SR2_PEC                8
+#define  STM_I2C_SR2_PEC_MASK  0xff00UL
+#define STM_I2C_SR2_DUALF      7
+#define STM_I2C_SR2_SMBHOST    6
+#define STM_I2C_SR2_SMBDEFAULT 5
+#define STM_I2C_SR2_GENCALL    4
+#define STM_I2C_SR2_TRA                2
+#define STM_I2C_SR2_BUSY               1
+#define STM_I2C_SR2_MSL                0
+
+#define STM_I2C_CCR_FS         15
+#define STM_I2C_CCR_DUTY       14
+#define STM_I2C_CCR_CCR                0
+#define  STM_I2C_CCR_MASK      0x7ffUL
+
+struct stm_adc {
+       vuint32_t       sr;
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       smpr1;
+
+       vuint32_t       smpr2;
+       vuint32_t       jofr1;
+       vuint32_t       jofr2;
+       vuint32_t       jofr3;
+
+       vuint32_t       jofr4;
+       vuint32_t       htr;
+       vuint32_t       ltr;
+       vuint32_t       sqr1;
+
+       vuint32_t       sqr2;
+       vuint32_t       sqr3;
+       vuint32_t       jsqr;
+       vuint32_t       jdr1;
+
+       vuint32_t       jdr2;
+       vuint32_t       jdr3;
+       vuint32_t       jdr4;
+       vuint32_t       dr;
+};
+
+extern struct stm_adc stm_adc1;
+
+//#define stm_adc1 (*((struct stm_adc *) 0x40012400))
+
+#define STM_ADC_SQ_TEMP                16
+#define STM_ADC_SQ_V_REF       17
+
+#define STM_ADC_SR_STRT                4
+#define STM_ADC_SR_JSTRT       3
+#define STM_ADC_SR_JEOC                2
+#define STM_ADC_SR_EOC         1
+#define STM_ADC_SR_AWD         0
+
+#define STM_ADC_CR1_AWDEN       23
+#define STM_ADC_CR1_JAWDEN     22
+#define STM_ADC_CR1_DUALMOD    16
+# define STM_ADC_CR1_DUALMOD_INDEPENDENT               0
+# define STM_ADC_CR1_DUALMOD_COMB_REG_SIM_INJ_SIM      1
+# define STM_ADC_CR1_DUALMOD_COMB_REG_SIM_ALT_TRIG     2
+# define STM_ADC_CR1_DUALMOD_COMB_INJ_SIM_FAST_INT     3
+# define STM_ADC_CR1_DUALMOD_COMB_INJ_SIM_SLOW_INT     4
+# define STM_ADC_CR1_DUALMOD_INJ_SIM                   5
+# define STM_ADC_CR1_DUALMOD_REG_SIM                   6
+# define STM_ADC_CR1_DUALMOD_FAST_INT                  7
+# define STM_ADC_CR1_DUALMOD_SLOW_INT                  8
+# define STM_ADC_CR1_DUALMOD_ALT_TRIG                  9
+
+#define STM_ADC_CR1_DISCNUM    13
+#define  STM_ADC_CR1_DISCNUM_1         0
+#define  STM_ADC_CR1_DISCNUM_2         1
+#define  STM_ADC_CR1_DISCNUM_3         2
+#define  STM_ADC_CR1_DISCNUM_4         3
+#define  STM_ADC_CR1_DISCNUM_5         4
+#define  STM_ADC_CR1_DISCNUM_6         5
+#define  STM_ADC_CR1_DISCNUM_7         6
+#define  STM_ADC_CR1_DISCNUM_8         7
+#define  STM_ADC_CR1_DISCNUM_MASK      7UL
+#define STM_ADC_CR1_JDISCEN    12
+#define STM_ADC_CR1_DISCEN     11
+#define STM_ADC_CR1_JAUTO      10
+#define STM_ADC_CR1_AWDSGL     9
+#define STM_ADC_CR1_SCAN       8
+#define STM_ADC_CR1_JEOCIE     7
+#define STM_ADC_CR1_AWDIE      6
+#define STM_ADC_CR1_EOCIE      5
+#define STM_ADC_CR1_AWDCH      0
+#define  STM_ADC_CR1_AWDCH_MASK                0x1fUL
+
+#define STM_ADC_CR2_TSVREFE    23
+#define STM_ADC_CR2_SWSTART    22
+#define STM_ADC_CR2_JWSTART    21
+#define STM_ADC_CR2_EXTTRIG    20
+#define STM_ADC_CR2_EXTSEL     17
+#define  STM_ADC_CR2_EXTSEL_TIM1_CC1   0
+#define  STM_ADC_CR2_EXTSEL_TIM1_CC2   1
+#define  STM_ADC_CR2_EXTSEL_TIM1_CC3   2
+#define  STM_ADC_CR2_EXTSEL_TIM2_CC2   3
+#define  STM_ADC_CR2_EXTSEL_TIM3_TRGO  4
+#define  STM_ADC_CR2_EXTSEL_TIM4_CC4   5
+#define  STM_ADC_CR2_EXTSEL_EXTI       6
+#define  STM_ADC_CR2_EXTSEL_SWSTART    7
+#define  STM_ADC_CR2_EXTSEL_MASK       7UL
+#define STM_ADC_CR2_JEXTTRIG   15
+#define STM_ADC_CR2_JEXTSEL    12
+#define  STM_ADC_CR2_JEXTSEL_TIM1_TRGO 0
+#define  STM_ADC_CR2_JEXTSEL_TIM1_CC4  1
+#define  STM_ADC_CR2_JEXTSEL_TIM2_TRGO 2
+#define  STM_ADC_CR2_JEXTSEL_TIM2_CC1  3
+#define  STM_ADC_CR2_JEXTSEL_TIM3_CC4  4
+#define  STM_ADC_CR2_JEXTSEL_TIM4_TRGO 5
+#define  STM_ADC_CR2_JEXTSEL_EXTI_15   6
+#define  STM_ADC_CR2_JEXTSEL_JSWSTART  7
+#define  STM_ADC_CR2_JEXTSEL_MASK      7UL
+#define STM_ADC_CR2_ALIGN      11
+#define STM_ADC_CR2_DMA                8
+#define STM_ADC_CR2_RSTCAL     3
+#define STM_ADC_CR2_CAL                2
+#define STM_ADC_CR2_CONT       1
+#define STM_ADC_CR2_ADON       0
+
+struct stm_exti {
+       vuint32_t       imr;
+       vuint32_t       emr;
+       vuint32_t       rtsr;
+       vuint32_t       ftsr;
+
+       vuint32_t       swier;
+       vuint32_t       pr;
+};
+
+extern struct stm_exti stm_exti;
+
+#define stm_exti       (*((struct stm_exti *) 0x40010400))
+
+static inline void
+stm_exticr_set(struct stm_gpio *gpio, int pin) {
+       uint8_t reg = (uint8_t) (pin >> 2);
+       uint8_t shift = (pin & 3) << 2;
+       uint8_t val = 0;
+
+       if (gpio == &stm_gpioa)
+               val = STM_AFIO_EXTICR_PA;
+       else if (gpio == &stm_gpiob)
+               val = STM_AFIO_EXTICR_PB;
+       else if (gpio == &stm_gpioc)
+               val = STM_AFIO_EXTICR_PC;
+       else if (gpio == &stm_gpiod)
+               val = STM_AFIO_EXTICR_PD;
+       else if (gpio == &stm_gpioe)
+               val = STM_AFIO_EXTICR_PE;
+
+       stm_afio.exticr[reg] = (stm_afio.exticr[reg] & (uint32_t) ~(0xf << shift)) | val << shift;
+}
+
+struct stm_tim18 {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       smcr;
+       vuint32_t       dier;
+
+       vuint32_t       sr;
+       vuint32_t       egr;
+       vuint32_t       ccmr1;
+       vuint32_t       ccmr2;
+
+       vuint32_t       ccer;
+       vuint32_t       cnt;
+       vuint32_t       psc;
+       vuint32_t       arr;
+
+       vuint32_t       rcr;
+       vuint32_t       ccr1;
+       vuint32_t       ccr2;
+       vuint32_t       ccr3;
+
+       vuint32_t       ccr4;
+       uint32_t        bdtr;
+       vuint32_t       dcr;
+       vuint32_t       dmar;
+};
+
+extern struct stm_tim18 stm_tim1, stm_tim8;
+
+#define stm_tim1       (*((struct stm_tim18 *) 0x40012c00))
+#define stm_tim8       (*((struct stm_tim18 *) 0x40013400))
+
+#define STM_TIM18_CR1_CKD      8
+#define  STM_TIM18_CR1_CKD_1           0
+#define  STM_TIM18_CR1_CKD_2           1
+#define  STM_TIM18_CR1_CKD_4           2
+#define  STM_TIM18_CR1_CKD_MASK        3UL
+#define STM_TIM18_CR1_ARPE     7
+#define STM_TIM18_CR1_CMS      5
+#define  STM_TIM18_CR1_CMS_EDGE        0
+#define  STM_TIM18_CR1_CMS_CENTER_1    1
+#define  STM_TIM18_CR1_CMS_CENTER_2    2
+#define  STM_TIM18_CR1_CMS_CENTER_3    3
+#define  STM_TIM18_CR1_CMS_MASK        3UL
+#define STM_TIM18_CR1_DIR      4
+#define  STM_TIM18_CR1_DIR_UP          0
+#define  STM_TIM18_CR1_DIR_DOWN        1
+#define STM_TIM18_CR1_OPM      3
+#define STM_TIM18_CR1_URS      2
+#define STM_TIM18_CR1_UDIS     1
+#define STM_TIM18_CR1_CEN      0
+
+#define STM_TIM18_CR2_TI1S     7
+#define STM_TIM18_CR2_MMS      4
+#define  STM_TIM18_CR2_MMS_RESET               0
+#define  STM_TIM18_CR2_MMS_ENABLE              1
+#define  STM_TIM18_CR2_MMS_UPDATE              2
+#define  STM_TIM18_CR2_MMS_COMPARE_PULSE       3
+#define  STM_TIM18_CR2_MMS_COMPARE_OC1REF      4
+#define  STM_TIM18_CR2_MMS_COMPARE_OC2REF      5
+#define  STM_TIM18_CR2_MMS_COMPARE_OC3REF      6
+#define  STM_TIM18_CR2_MMS_COMPARE_OC4REF      7
+#define  STM_TIM18_CR2_MMS_MASK                7UL
+#define STM_TIM18_CR2_CCDS     3
+
+#define STM_TIM18_SMCR_ETP     15
+#define STM_TIM18_SMCR_ECE     14
+#define STM_TIM18_SMCR_ETPS    12
+#define  STM_TIM18_SMCR_ETPS_OFF               0
+#define  STM_TIM18_SMCR_ETPS_DIV_2             1
+#define  STM_TIM18_SMCR_ETPS_DIV_4             2
+#define  STM_TIM18_SMCR_ETPS_DIV_8             3
+#define  STM_TIM18_SMCR_ETPS_MASK              3UL
+#define STM_TIM18_SMCR_ETF     8
+#define  STM_TIM18_SMCR_ETF_NONE               0
+#define  STM_TIM18_SMCR_ETF_INT_N_2            1
+#define  STM_TIM18_SMCR_ETF_INT_N_4            2
+#define  STM_TIM18_SMCR_ETF_INT_N_8            3
+#define  STM_TIM18_SMCR_ETF_DTS_2_N_6          4
+#define  STM_TIM18_SMCR_ETF_DTS_2_N_8          5
+#define  STM_TIM18_SMCR_ETF_DTS_4_N_6          6
+#define  STM_TIM18_SMCR_ETF_DTS_4_N_8          7
+#define  STM_TIM18_SMCR_ETF_DTS_8_N_6          8
+#define  STM_TIM18_SMCR_ETF_DTS_8_N_8          9
+#define  STM_TIM18_SMCR_ETF_DTS_16_N_5         10
+#define  STM_TIM18_SMCR_ETF_DTS_16_N_6         11
+#define  STM_TIM18_SMCR_ETF_DTS_16_N_8         12
+#define  STM_TIM18_SMCR_ETF_DTS_32_N_5         13
+#define  STM_TIM18_SMCR_ETF_DTS_32_N_6         14
+#define  STM_TIM18_SMCR_ETF_DTS_32_N_8         15
+#define  STM_TIM18_SMCR_ETF_MASK               15UL
+#define STM_TIM18_SMCR_MSM     7
+#define STM_TIM18_SMCR_TS      4
+#define  STM_TIM18_SMCR_TS_ITR0                0
+#define  STM_TIM18_SMCR_TS_ITR1                1
+#define  STM_TIM18_SMCR_TS_ITR2                2
+#define  STM_TIM18_SMCR_TS_ITR3                3
+#define  STM_TIM18_SMCR_TS_TI1F_ED             4
+#define  STM_TIM18_SMCR_TS_TI1FP1              5
+#define  STM_TIM18_SMCR_TS_TI2FP2              6
+#define  STM_TIM18_SMCR_TS_ETRF                7
+#define  STM_TIM18_SMCR_TS_MASK                7UL
+#define STM_TIM18_SMCR_SMS     0
+#define  STM_TIM18_SMCR_SMS_DISABLE            0
+#define  STM_TIM18_SMCR_SMS_ENCODER_MODE_1     1
+#define  STM_TIM18_SMCR_SMS_ENCODER_MODE_2     2
+#define  STM_TIM18_SMCR_SMS_ENCODER_MODE_3     3
+#define  STM_TIM18_SMCR_SMS_RESET_MODE         4
+#define  STM_TIM18_SMCR_SMS_GATED_MODE         5
+#define  STM_TIM18_SMCR_SMS_TRIGGER_MODE       6
+#define  STM_TIM18_SMCR_SMS_EXTERNAL_CLOCK     7
+#define  STM_TIM18_SMCR_SMS_MASK               7UL
+
+#define STM_TIM18_DIER_TDE             14
+#define STM_TIM18_DIER_CC4DE           12
+#define STM_TIM18_DIER_CC3DE           11
+#define STM_TIM18_DIER_CC2DE           10
+#define STM_TIM18_DIER_CC1DE           9
+#define STM_TIM18_DIER_UDE             8
+
+#define STM_TIM18_DIER_TIE             6
+#define STM_TIM18_DIER_CC4IE           4
+#define STM_TIM18_DIER_CC3IE           3
+#define STM_TIM18_DIER_CC2IE           2
+#define STM_TIM18_DIER_CC1IE           1
+#define STM_TIM18_DIER_UIE             0
+
+#define STM_TIM18_SR_CC4OF     12
+#define STM_TIM18_SR_CC3OF     11
+#define STM_TIM18_SR_CC2OF     10
+#define STM_TIM18_SR_CC1OF     9
+#define STM_TIM18_SR_TIF       6
+#define STM_TIM18_SR_CC4IF     4
+#define STM_TIM18_SR_CC3IF     3
+#define STM_TIM18_SR_CC2IF     2
+#define STM_TIM18_SR_CC1IF     1
+#define STM_TIM18_SR_UIF       0
+
+#define STM_TIM18_EGR_TG       6
+#define STM_TIM18_EGR_CC4G     4
+#define STM_TIM18_EGR_CC3G     3
+#define STM_TIM18_EGR_CC2G     2
+#define STM_TIM18_EGR_CC1G     1
+#define STM_TIM18_EGR_UG       0
+
+#define STM_TIM18_CCMR1_OC2CE  15
+#define STM_TIM18_CCMR1_OC2M   12
+#define  STM_TIM18_CCMR1_OC2M_FROZEN                   0
+#define  STM_TIM18_CCMR1_OC2M_SET_HIGH_ON_MATCH        1
+#define  STM_TIM18_CCMR1_OC2M_SET_LOW_ON_MATCH         2
+#define  STM_TIM18_CCMR1_OC2M_TOGGLE                   3
+#define  STM_TIM18_CCMR1_OC2M_FORCE_LOW                4
+#define  STM_TIM18_CCMR1_OC2M_FORCE_HIGH               5
+#define  STM_TIM18_CCMR1_OC2M_PWM_MODE_1               6
+#define  STM_TIM18_CCMR1_OC2M_PWM_MODE_2               7
+#define  STM_TIM18_CCMR1_OC2M_MASK                     7UL
+#define STM_TIM18_CCMR1_OC2PE  11
+#define STM_TIM18_CCMR1_OC2FE  10
+#define STM_TIM18_CCMR1_CC2S   8
+#define  STM_TIM18_CCMR1_CC2S_OUTPUT                   0
+#define  STM_TIM18_CCMR1_CC2S_INPUT_TI2                1
+#define  STM_TIM18_CCMR1_CC2S_INPUT_TI1                2
+#define  STM_TIM18_CCMR1_CC2S_INPUT_TRC                3
+#define  STM_TIM18_CCMR1_CC2S_MASK                     3UL
+
+#define STM_TIM18_CCMR1_OC1CE  7
+#define STM_TIM18_CCMR1_OC1M   4
+#define  STM_TIM18_CCMR1_OC1M_FROZEN                   0
+#define  STM_TIM18_CCMR1_OC1M_SET_HIGH_ON_MATCH        1
+#define  STM_TIM18_CCMR1_OC1M_SET_LOW_ON_MATCH         2
+#define  STM_TIM18_CCMR1_OC1M_TOGGLE                   3
+#define  STM_TIM18_CCMR1_OC1M_FORCE_LOW                4
+#define  STM_TIM18_CCMR1_OC1M_FORCE_HIGH               5
+#define  STM_TIM18_CCMR1_OC1M_PWM_MODE_1               6
+#define  STM_TIM18_CCMR1_OC1M_PWM_MODE_2               7
+#define  STM_TIM18_CCMR1_OC1M_MASK                     7UL
+#define STM_TIM18_CCMR1_OC1PE  3
+#define STM_TIM18_CCMR1_OC1FE  2
+#define STM_TIM18_CCMR1_CC1S   0
+#define  STM_TIM18_CCMR1_CC1S_OUTPUT                   0
+#define  STM_TIM18_CCMR1_CC1S_INPUT_TI1                1
+#define  STM_TIM18_CCMR1_CC1S_INPUT_TI2                2
+#define  STM_TIM18_CCMR1_CC1S_INPUT_TRC                3
+#define  STM_TIM18_CCMR1_CC1S_MASK                     3UL
+
+#define STM_TIM18_CCMR1_IC2F   12
+#define  STM_TIM18_CCMR1_IC2F_NONE                     0
+#define  STM_TIM18_CCMR1_IC2F_CK_INT_N_2               1
+#define  STM_TIM18_CCMR1_IC2F_CK_INT_N_4               2
+#define  STM_TIM18_CCMR1_IC2F_CK_INT_N_8               3
+#define  STM_TIM18_CCMR1_IC2F_DTS_2_N_6                4
+#define  STM_TIM18_CCMR1_IC2F_DTS_2_N_8                5
+#define  STM_TIM18_CCMR1_IC2F_DTS_4_N_6                6
+#define  STM_TIM18_CCMR1_IC2F_DTS_4_N_8                7
+#define  STM_TIM18_CCMR1_IC2F_DTS_8_N_6                8
+#define  STM_TIM18_CCMR1_IC2F_DTS_8_N_8                9
+#define  STM_TIM18_CCMR1_IC2F_DTS_16_N_5               10
+#define  STM_TIM18_CCMR1_IC2F_DTS_16_N_6               11
+#define  STM_TIM18_CCMR1_IC2F_DTS_16_N_8               12
+#define  STM_TIM18_CCMR1_IC2F_DTS_32_N_5               13
+#define  STM_TIM18_CCMR1_IC2F_DTS_32_N_6               14
+#define  STM_TIM18_CCMR1_IC2F_DTS_32_N_8               15
+#define STM_TIM18_CCMR1_IC2PSC 10
+#define  STM_TIM18_CCMR1_IC2PSC_NONE                   0
+#define  STM_TIM18_CCMR1_IC2PSC_2                      1
+#define  STM_TIM18_CCMR1_IC2PSC_4                      2
+#define  STM_TIM18_CCMR1_IC2PSC_8                      3
+#define STM_TIM18_CCMR1_IC1F   4
+#define  STM_TIM18_CCMR1_IC1F_NONE                     0
+#define  STM_TIM18_CCMR1_IC1F_CK_INT_N_2               1
+#define  STM_TIM18_CCMR1_IC1F_CK_INT_N_4               2
+#define  STM_TIM18_CCMR1_IC1F_CK_INT_N_8               3
+#define  STM_TIM18_CCMR1_IC1F_DTS_2_N_6                4
+#define  STM_TIM18_CCMR1_IC1F_DTS_2_N_8                5
+#define  STM_TIM18_CCMR1_IC1F_DTS_4_N_6                6
+#define  STM_TIM18_CCMR1_IC1F_DTS_4_N_8                7
+#define  STM_TIM18_CCMR1_IC1F_DTS_8_N_6                8
+#define  STM_TIM18_CCMR1_IC1F_DTS_8_N_8                9
+#define  STM_TIM18_CCMR1_IC1F_DTS_16_N_5               10
+#define  STM_TIM18_CCMR1_IC1F_DTS_16_N_6               11
+#define  STM_TIM18_CCMR1_IC1F_DTS_16_N_8               12
+#define  STM_TIM18_CCMR1_IC1F_DTS_32_N_5               13
+#define  STM_TIM18_CCMR1_IC1F_DTS_32_N_6               14
+#define  STM_TIM18_CCMR1_IC1F_DTS_32_N_8               15
+#define STM_TIM18_CCMR1_IC1PSC 2
+#define  STM_TIM18_CCMR1_IC1PSC_NONE                   0
+#define  STM_TIM18_CCMR1_IC1PSC_2                      1
+#define  STM_TIM18_CCMR1_IC1PSC_4                      2
+#define  STM_TIM18_CCMR1_IC1PSC_8                      3
+
+#define STM_TIM18_CCMR2_OC4CE  15
+#define STM_TIM18_CCMR2_OC4M   12
+#define  STM_TIM18_CCMR2_OC4M_FROZEN                   0
+#define  STM_TIM18_CCMR2_OC4M_SET_HIGH_ON_MATCH        1
+#define  STM_TIM18_CCMR2_OC4M_SET_LOW_ON_MATCH         2
+#define  STM_TIM18_CCMR2_OC4M_TOGGLE                   3
+#define  STM_TIM18_CCMR2_OC4M_FORCE_LOW                4
+#define  STM_TIM18_CCMR2_OC4M_FORCE_HIGH               5
+#define  STM_TIM18_CCMR2_OC4M_PWM_MODE_1               6
+#define  STM_TIM18_CCMR2_OC4M_PWM_MODE_2               7
+#define  STM_TIM18_CCMR2_OC4M_MASK                     7UL
+#define STM_TIM18_CCMR2_OC4PE  11
+#define STM_TIM18_CCMR2_OC4FE  10
+#define STM_TIM18_CCMR2_CC4S   8
+#define  STM_TIM18_CCMR2_CC4S_OUTPUT                   0
+#define  STM_TIM18_CCMR2_CC4S_INPUT_TI4                1
+#define  STM_TIM18_CCMR2_CC4S_INPUT_TI3                2
+#define  STM_TIM18_CCMR2_CC4S_INPUT_TRC                3
+#define  STM_TIM18_CCMR2_CC4S_MASK                     3UL
+
+#define STM_TIM18_CCMR2_OC3CE  7
+#define STM_TIM18_CCMR2_OC3M   4
+#define  STM_TIM18_CCMR2_OC3M_FROZEN                   0
+#define  STM_TIM18_CCMR2_OC3M_SET_HIGH_ON_MATCH        1
+#define  STM_TIM18_CCMR2_OC3M_SET_LOW_ON_MATCH         2
+#define  STM_TIM18_CCMR2_OC3M_TOGGLE                   3
+#define  STM_TIM18_CCMR2_OC3M_FORCE_LOW                4
+#define  STM_TIM18_CCMR2_OC3M_FORCE_HIGH               5
+#define  STM_TIM18_CCMR2_OC3M_PWM_MODE_1               6
+#define  STM_TIM18_CCMR2_OC3M_PWM_MODE_2               7
+#define  STM_TIM18_CCMR2_OC3M_MASK                     7UL
+#define STM_TIM18_CCMR2_OC3PE  3
+#define STM_TIM18_CCMR2_OC3FE  2
+#define STM_TIM18_CCMR2_CC3S   0
+#define  STM_TIM18_CCMR2_CC3S_OUTPUT                   0
+#define  STM_TIM18_CCMR2_CC3S_INPUT_TI3                1
+#define  STM_TIM18_CCMR2_CC3S_INPUT_TI4                2
+#define  STM_TIM18_CCMR2_CC3S_INPUT_TRC                3
+#define  STM_TIM18_CCMR2_CC3S_MASK                     3UL
+
+#define STM_TIM18_CCER_CC4NP   15
+#define STM_TIM18_CCER_CC4P    13
+#define  STM_TIM18_CCER_CC4P_ACTIVE_HIGH       0
+#define  STM_TIM18_CCER_CC4P_ACTIVE_LOW        1
+#define STM_TIM18_CCER_CC4E    12
+#define STM_TIM18_CCER_CC3NP   11
+#define STM_TIM18_CCER_CC3P    9
+#define  STM_TIM18_CCER_CC3P_ACTIVE_HIGH       0
+#define  STM_TIM18_CCER_CC3P_ACTIVE_LOW        1
+#define STM_TIM18_CCER_CC3E    8
+#define STM_TIM18_CCER_CC2NP   7
+#define STM_TIM18_CCER_CC2P    5
+#define  STM_TIM18_CCER_CC2P_ACTIVE_HIGH       0
+#define  STM_TIM18_CCER_CC2P_ACTIVE_LOW        1
+#define STM_TIM18_CCER_CC2E    4
+#define STM_TIM18_CCER_CC1NP   3
+#define STM_TIM18_CCER_CC1P    1
+#define  STM_TIM18_CCER_CC1P_ACTIVE_HIGH       0
+#define  STM_TIM18_CCER_CC1P_ACTIVE_LOW        1
+#define STM_TIM18_CCER_CC1E    0
+
+struct stm_tim234 {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       smcr;
+       vuint32_t       dier;
+
+       vuint32_t       sr;
+       vuint32_t       egr;
+       vuint32_t       ccmr1;
+       vuint32_t       ccmr2;
+
+       vuint32_t       ccer;
+       vuint32_t       cnt;
+       vuint32_t       psc;
+       vuint32_t       arr;
+
+       uint32_t        reserved_30;
+       vuint32_t       ccr1;
+       vuint32_t       ccr2;
+       vuint32_t       ccr3;
+
+       vuint32_t       ccr4;
+       uint32_t        reserved_44;
+       vuint32_t       dcr;
+       vuint32_t       dmar;
+};
+
+extern struct stm_tim234 stm_tim2, stm_tim3, stm_tim4;
+
+#define stm_tim2       (*((struct stm_tim234 *) 0x40000000))
+#define stm_tim3       (*((struct stm_tim234 *) 0x40000400))
+#define stm_tim4       (*((struct stm_tim234 *) 0x40000800))
+
+#define STM_TIM234_CR1_CKD     8
+#define  STM_TIM234_CR1_CKD_1          0
+#define  STM_TIM234_CR1_CKD_2          1
+#define  STM_TIM234_CR1_CKD_4          2
+#define  STM_TIM234_CR1_CKD_MASK       3UL
+#define STM_TIM234_CR1_ARPE    7
+#define STM_TIM234_CR1_CMS     5
+#define  STM_TIM234_CR1_CMS_EDGE       0
+#define  STM_TIM234_CR1_CMS_CENTER_1   1
+#define  STM_TIM234_CR1_CMS_CENTER_2   2
+#define  STM_TIM234_CR1_CMS_CENTER_3   3
+#define  STM_TIM234_CR1_CMS_MASK       3UL
+#define STM_TIM234_CR1_DIR     4
+#define  STM_TIM234_CR1_DIR_UP         0
+#define  STM_TIM234_CR1_DIR_DOWN       1
+#define STM_TIM234_CR1_OPM     3
+#define STM_TIM234_CR1_URS     2
+#define STM_TIM234_CR1_UDIS    1
+#define STM_TIM234_CR1_CEN     0
+
+#define STM_TIM234_CR2_TI1S    7
+#define STM_TIM234_CR2_MMS     4
+#define  STM_TIM234_CR2_MMS_RESET              0
+#define  STM_TIM234_CR2_MMS_ENABLE             1
+#define  STM_TIM234_CR2_MMS_UPDATE             2
+#define  STM_TIM234_CR2_MMS_COMPARE_PULSE      3
+#define  STM_TIM234_CR2_MMS_COMPARE_OC1REF     4
+#define  STM_TIM234_CR2_MMS_COMPARE_OC2REF     5
+#define  STM_TIM234_CR2_MMS_COMPARE_OC3REF     6
+#define  STM_TIM234_CR2_MMS_COMPARE_OC4REF     7
+#define  STM_TIM234_CR2_MMS_MASK               7UL
+#define STM_TIM234_CR2_CCDS    3
+
+#define STM_TIM234_SMCR_ETP    15
+#define STM_TIM234_SMCR_ECE    14
+#define STM_TIM234_SMCR_ETPS   12
+#define  STM_TIM234_SMCR_ETPS_OFF              0
+#define  STM_TIM234_SMCR_ETPS_DIV_2            1
+#define  STM_TIM234_SMCR_ETPS_DIV_4            2
+#define  STM_TIM234_SMCR_ETPS_DIV_8            3
+#define  STM_TIM234_SMCR_ETPS_MASK             3UL
+#define STM_TIM234_SMCR_ETF    8
+#define  STM_TIM234_SMCR_ETF_NONE              0
+#define  STM_TIM234_SMCR_ETF_INT_N_2           1
+#define  STM_TIM234_SMCR_ETF_INT_N_4           2
+#define  STM_TIM234_SMCR_ETF_INT_N_8           3
+#define  STM_TIM234_SMCR_ETF_DTS_2_N_6         4
+#define  STM_TIM234_SMCR_ETF_DTS_2_N_8         5
+#define  STM_TIM234_SMCR_ETF_DTS_4_N_6         6
+#define  STM_TIM234_SMCR_ETF_DTS_4_N_8         7
+#define  STM_TIM234_SMCR_ETF_DTS_8_N_6         8
+#define  STM_TIM234_SMCR_ETF_DTS_8_N_8         9
+#define  STM_TIM234_SMCR_ETF_DTS_16_N_5                10
+#define  STM_TIM234_SMCR_ETF_DTS_16_N_6                11
+#define  STM_TIM234_SMCR_ETF_DTS_16_N_8                12
+#define  STM_TIM234_SMCR_ETF_DTS_32_N_5                13
+#define  STM_TIM234_SMCR_ETF_DTS_32_N_6                14
+#define  STM_TIM234_SMCR_ETF_DTS_32_N_8                15
+#define  STM_TIM234_SMCR_ETF_MASK              15UL
+#define STM_TIM234_SMCR_MSM    7
+#define STM_TIM234_SMCR_TS     4
+#define  STM_TIM234_SMCR_TS_ITR0               0
+#define  STM_TIM234_SMCR_TS_ITR1               1
+#define  STM_TIM234_SMCR_TS_ITR2               2
+#define  STM_TIM234_SMCR_TS_ITR3               3
+#define  STM_TIM234_SMCR_TS_TI1F_ED            4
+#define  STM_TIM234_SMCR_TS_TI1FP1             5
+#define  STM_TIM234_SMCR_TS_TI2FP2             6
+#define  STM_TIM234_SMCR_TS_ETRF               7
+#define  STM_TIM234_SMCR_TS_MASK               7UL
+#define STM_TIM234_SMCR_SMS    0
+#define  STM_TIM234_SMCR_SMS_DISABLE           0
+#define  STM_TIM234_SMCR_SMS_ENCODER_MODE_1    1
+#define  STM_TIM234_SMCR_SMS_ENCODER_MODE_2    2
+#define  STM_TIM234_SMCR_SMS_ENCODER_MODE_3    3
+#define  STM_TIM234_SMCR_SMS_RESET_MODE                4
+#define  STM_TIM234_SMCR_SMS_GATED_MODE                5
+#define  STM_TIM234_SMCR_SMS_TRIGGER_MODE      6
+#define  STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK    7
+#define  STM_TIM234_SMCR_SMS_MASK              7UL
+
+#define STM_TIM234_DIER_TDE            14
+#define STM_TIM234_DIER_CC4DE          12
+#define STM_TIM234_DIER_CC3DE          11
+#define STM_TIM234_DIER_CC2DE          10
+#define STM_TIM234_DIER_CC1DE          9
+#define STM_TIM234_DIER_UDE            8
+
+#define STM_TIM234_DIER_TIE            6
+#define STM_TIM234_DIER_CC4IE          4
+#define STM_TIM234_DIER_CC3IE          3
+#define STM_TIM234_DIER_CC2IE          2
+#define STM_TIM234_DIER_CC1IE          1
+#define STM_TIM234_DIER_UIE            0
+
+#define STM_TIM234_SR_CC4OF    12
+#define STM_TIM234_SR_CC3OF    11
+#define STM_TIM234_SR_CC2OF    10
+#define STM_TIM234_SR_CC1OF    9
+#define STM_TIM234_SR_TIF      6
+#define STM_TIM234_SR_CC4IF    4
+#define STM_TIM234_SR_CC3IF    3
+#define STM_TIM234_SR_CC2IF    2
+#define STM_TIM234_SR_CC1IF    1
+#define STM_TIM234_SR_UIF      0
+
+#define STM_TIM234_EGR_TG      6
+#define STM_TIM234_EGR_CC4G    4
+#define STM_TIM234_EGR_CC3G    3
+#define STM_TIM234_EGR_CC2G    2
+#define STM_TIM234_EGR_CC1G    1
+#define STM_TIM234_EGR_UG      0
+
+#define STM_TIM234_CCMR1_OC2CE 15
+#define STM_TIM234_CCMR1_OC2M  12
+#define  STM_TIM234_CCMR1_OC2M_FROZEN                  0
+#define  STM_TIM234_CCMR1_OC2M_SET_HIGH_ON_MATCH       1
+#define  STM_TIM234_CCMR1_OC2M_SET_LOW_ON_MATCH                2
+#define  STM_TIM234_CCMR1_OC2M_TOGGLE                  3
+#define  STM_TIM234_CCMR1_OC2M_FORCE_LOW               4
+#define  STM_TIM234_CCMR1_OC2M_FORCE_HIGH              5
+#define  STM_TIM234_CCMR1_OC2M_PWM_MODE_1              6
+#define  STM_TIM234_CCMR1_OC2M_PWM_MODE_2              7
+#define  STM_TIM234_CCMR1_OC2M_MASK                    7UL
+#define STM_TIM234_CCMR1_OC2PE 11
+#define STM_TIM234_CCMR1_OC2FE 10
+#define STM_TIM234_CCMR1_CC2S  8
+#define  STM_TIM234_CCMR1_CC2S_OUTPUT                  0
+#define  STM_TIM234_CCMR1_CC2S_INPUT_TI2               1
+#define  STM_TIM234_CCMR1_CC2S_INPUT_TI1               2
+#define  STM_TIM234_CCMR1_CC2S_INPUT_TRC               3
+#define  STM_TIM234_CCMR1_CC2S_MASK                    3UL
+
+#define STM_TIM234_CCMR1_OC1CE 7
+#define STM_TIM234_CCMR1_OC1M  4
+#define  STM_TIM234_CCMR1_OC1M_FROZEN                  0
+#define  STM_TIM234_CCMR1_OC1M_SET_HIGH_ON_MATCH       1
+#define  STM_TIM234_CCMR1_OC1M_SET_LOW_ON_MATCH                2
+#define  STM_TIM234_CCMR1_OC1M_TOGGLE                  3
+#define  STM_TIM234_CCMR1_OC1M_FORCE_LOW               4
+#define  STM_TIM234_CCMR1_OC1M_FORCE_HIGH              5
+#define  STM_TIM234_CCMR1_OC1M_PWM_MODE_1              6
+#define  STM_TIM234_CCMR1_OC1M_PWM_MODE_2              7
+#define  STM_TIM234_CCMR1_OC1M_MASK                    7UL
+#define STM_TIM234_CCMR1_OC1PE 3
+#define STM_TIM234_CCMR1_OC1FE 2
+#define STM_TIM234_CCMR1_CC1S  0
+#define  STM_TIM234_CCMR1_CC1S_OUTPUT                  0
+#define  STM_TIM234_CCMR1_CC1S_INPUT_TI1               1
+#define  STM_TIM234_CCMR1_CC1S_INPUT_TI2               2
+#define  STM_TIM234_CCMR1_CC1S_INPUT_TRC               3
+#define  STM_TIM234_CCMR1_CC1S_MASK                    3UL
+
+#define STM_TIM234_CCMR1_IC2F  12
+#define  STM_TIM234_CCMR1_IC2F_NONE                    0
+#define  STM_TIM234_CCMR1_IC2F_CK_INT_N_2              1
+#define  STM_TIM234_CCMR1_IC2F_CK_INT_N_4              2
+#define  STM_TIM234_CCMR1_IC2F_CK_INT_N_8              3
+#define  STM_TIM234_CCMR1_IC2F_DTS_2_N_6               4
+#define  STM_TIM234_CCMR1_IC2F_DTS_2_N_8               5
+#define  STM_TIM234_CCMR1_IC2F_DTS_4_N_6               6
+#define  STM_TIM234_CCMR1_IC2F_DTS_4_N_8               7
+#define  STM_TIM234_CCMR1_IC2F_DTS_8_N_6               8
+#define  STM_TIM234_CCMR1_IC2F_DTS_8_N_8               9
+#define  STM_TIM234_CCMR1_IC2F_DTS_16_N_5              10
+#define  STM_TIM234_CCMR1_IC2F_DTS_16_N_6              11
+#define  STM_TIM234_CCMR1_IC2F_DTS_16_N_8              12
+#define  STM_TIM234_CCMR1_IC2F_DTS_32_N_5              13
+#define  STM_TIM234_CCMR1_IC2F_DTS_32_N_6              14
+#define  STM_TIM234_CCMR1_IC2F_DTS_32_N_8              15
+#define STM_TIM234_CCMR1_IC2PSC        10
+#define  STM_TIM234_CCMR1_IC2PSC_NONE                  0
+#define  STM_TIM234_CCMR1_IC2PSC_2                     1
+#define  STM_TIM234_CCMR1_IC2PSC_4                     2
+#define  STM_TIM234_CCMR1_IC2PSC_8                     3
+#define STM_TIM234_CCMR1_IC1F  4
+#define  STM_TIM234_CCMR1_IC1F_NONE                    0
+#define  STM_TIM234_CCMR1_IC1F_CK_INT_N_2              1
+#define  STM_TIM234_CCMR1_IC1F_CK_INT_N_4              2
+#define  STM_TIM234_CCMR1_IC1F_CK_INT_N_8              3
+#define  STM_TIM234_CCMR1_IC1F_DTS_2_N_6               4
+#define  STM_TIM234_CCMR1_IC1F_DTS_2_N_8               5
+#define  STM_TIM234_CCMR1_IC1F_DTS_4_N_6               6
+#define  STM_TIM234_CCMR1_IC1F_DTS_4_N_8               7
+#define  STM_TIM234_CCMR1_IC1F_DTS_8_N_6               8
+#define  STM_TIM234_CCMR1_IC1F_DTS_8_N_8               9
+#define  STM_TIM234_CCMR1_IC1F_DTS_16_N_5              10
+#define  STM_TIM234_CCMR1_IC1F_DTS_16_N_6              11
+#define  STM_TIM234_CCMR1_IC1F_DTS_16_N_8              12
+#define  STM_TIM234_CCMR1_IC1F_DTS_32_N_5              13
+#define  STM_TIM234_CCMR1_IC1F_DTS_32_N_6              14
+#define  STM_TIM234_CCMR1_IC1F_DTS_32_N_8              15
+#define STM_TIM234_CCMR1_IC1PSC        2
+#define  STM_TIM234_CCMR1_IC1PSC_NONE                  0
+#define  STM_TIM234_CCMR1_IC1PSC_2                     1
+#define  STM_TIM234_CCMR1_IC1PSC_4                     2
+#define  STM_TIM234_CCMR1_IC1PSC_8                     3
+
+#define STM_TIM234_CCMR2_OC4CE 15
+#define STM_TIM234_CCMR2_OC4M  12
+#define  STM_TIM234_CCMR2_OC4M_FROZEN                  0
+#define  STM_TIM234_CCMR2_OC4M_SET_HIGH_ON_MATCH       1
+#define  STM_TIM234_CCMR2_OC4M_SET_LOW_ON_MATCH                2
+#define  STM_TIM234_CCMR2_OC4M_TOGGLE                  3
+#define  STM_TIM234_CCMR2_OC4M_FORCE_LOW               4
+#define  STM_TIM234_CCMR2_OC4M_FORCE_HIGH              5
+#define  STM_TIM234_CCMR2_OC4M_PWM_MODE_1              6
+#define  STM_TIM234_CCMR2_OC4M_PWM_MODE_2              7
+#define  STM_TIM234_CCMR2_OC4M_MASK                    7UL
+#define STM_TIM234_CCMR2_OC4PE 11
+#define STM_TIM234_CCMR2_OC4FE 10
+#define STM_TIM234_CCMR2_CC4S  8
+#define  STM_TIM234_CCMR2_CC4S_OUTPUT                  0
+#define  STM_TIM234_CCMR2_CC4S_INPUT_TI4               1
+#define  STM_TIM234_CCMR2_CC4S_INPUT_TI3               2
+#define  STM_TIM234_CCMR2_CC4S_INPUT_TRC               3
+#define  STM_TIM234_CCMR2_CC4S_MASK                    3UL
+
+#define STM_TIM234_CCMR2_OC3CE 7
+#define STM_TIM234_CCMR2_OC3M  4
+#define  STM_TIM234_CCMR2_OC3M_FROZEN                  0
+#define  STM_TIM234_CCMR2_OC3M_SET_HIGH_ON_MATCH       1
+#define  STM_TIM234_CCMR2_OC3M_SET_LOW_ON_MATCH                2
+#define  STM_TIM234_CCMR2_OC3M_TOGGLE                  3
+#define  STM_TIM234_CCMR2_OC3M_FORCE_LOW               4
+#define  STM_TIM234_CCMR2_OC3M_FORCE_HIGH              5
+#define  STM_TIM234_CCMR2_OC3M_PWM_MODE_1              6
+#define  STM_TIM234_CCMR2_OC3M_PWM_MODE_2              7
+#define  STM_TIM234_CCMR2_OC3M_MASK                    7UL
+#define STM_TIM234_CCMR2_OC3PE 3
+#define STM_TIM234_CCMR2_OC3FE 2
+#define STM_TIM234_CCMR2_CC3S  0
+#define  STM_TIM234_CCMR2_CC3S_OUTPUT                  0
+#define  STM_TIM234_CCMR2_CC3S_INPUT_TI3               1
+#define  STM_TIM234_CCMR2_CC3S_INPUT_TI4               2
+#define  STM_TIM234_CCMR2_CC3S_INPUT_TRC               3
+#define  STM_TIM234_CCMR2_CC3S_MASK                    3UL
+
+#define STM_TIM234_CCER_CC4NP  15
+#define STM_TIM234_CCER_CC4P   13
+#define  STM_TIM234_CCER_CC4P_ACTIVE_HIGH      0
+#define  STM_TIM234_CCER_CC4P_ACTIVE_LOW       1
+#define STM_TIM234_CCER_CC4E   12
+#define STM_TIM234_CCER_CC3NP  11
+#define STM_TIM234_CCER_CC3P   9
+#define  STM_TIM234_CCER_CC3P_ACTIVE_HIGH      0
+#define  STM_TIM234_CCER_CC3P_ACTIVE_LOW       1
+#define STM_TIM234_CCER_CC3E   8
+#define STM_TIM234_CCER_CC2NP  7
+#define STM_TIM234_CCER_CC2P   5
+#define  STM_TIM234_CCER_CC2P_ACTIVE_HIGH      0
+#define  STM_TIM234_CCER_CC2P_ACTIVE_LOW       1
+#define STM_TIM234_CCER_CC2E   4
+#define STM_TIM234_CCER_CC1NP  3
+#define STM_TIM234_CCER_CC1P   1
+#define  STM_TIM234_CCER_CC1P_ACTIVE_HIGH      0
+#define  STM_TIM234_CCER_CC1P_ACTIVE_LOW       1
+#define STM_TIM234_CCER_CC1E   0
+
+struct stm_tim67 {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       uint32_t        _unused_08;
+       vuint32_t       dier;
+
+       vuint32_t       sr;
+       vuint32_t       egr;
+       uint32_t        _unused_18;
+       uint32_t        _unused_1c;
+
+       uint32_t        _unused_20;
+       vuint32_t       cnt;
+       vuint32_t       psc;
+       vuint32_t       arr;
+};
+
+extern struct stm_tim67 stm_tim6;
+
+#define STM_TIM67_CR1_ARPE     (7)
+#define STM_TIM67_CR1_OPM      (3)
+#define STM_TIM67_CR1_URS      (2)
+#define STM_TIM67_CR1_UDIS     (1)
+#define STM_TIM67_CR1_CEN      (0)
+
+#define STM_TIM67_CR2_MMS      (4)
+#define  STM_TIM67_CR2_MMS_RESET       0
+#define  STM_TIM67_CR2_MMS_ENABLE      1
+#define  STM_TIM67_CR2_MMS_UPDATE      2
+#define  STM_TIM67_CR2_MMS_MASK                7UL
+
+#define STM_TIM67_DIER_UDE     (8)
+#define STM_TIM67_DIER_UIE     (0)
+
+#define STM_TIM67_SR_UIF       (0)
+
+#define STM_TIM67_EGR_UG       (0)
+
+#define isr_decl(name) void stm_ ## name ## _isr(void)
+
+isr_decl(halt);
+isr_decl(ignore);
+
+isr_decl(nmi);
+isr_decl(hardfault);
+isr_decl(memmanage);
+isr_decl(busfault);
+isr_decl(usagefault);
+isr_decl(svc);
+isr_decl(debugmon);
+isr_decl(pendsv);
+isr_decl(systick);
+isr_decl(wwdg);
+isr_decl(pvd);
+isr_decl(tamper_stamp);
+isr_decl(rtc_wkup);
+isr_decl(flash);
+isr_decl(rcc);
+isr_decl(exti0);
+isr_decl(exti1);
+isr_decl(exti2);
+isr_decl(exti3);
+isr_decl(exti4);
+isr_decl(dma1_channel1);
+isr_decl(dma1_channel2);
+isr_decl(dma1_channel3);
+isr_decl(dma1_channel4);
+isr_decl(dma1_channel5);
+isr_decl(dma1_channel6);
+isr_decl(dma1_channel7);
+isr_decl(adc1_2);
+isr_decl(usb_hp);
+isr_decl(usb_lp);
+isr_decl(can_rx1);
+isr_decl(can_sce);
+isr_decl(exti9_5);
+isr_decl(tim1_brk);
+isr_decl(tim1_up);
+isr_decl(tim1_trg_com);
+isr_decl(tim1_cc);
+isr_decl(tim2);
+isr_decl(tim3);
+isr_decl(tim4);
+isr_decl(i2c1_ev);
+isr_decl(i2c1_er);
+isr_decl(i2c2_ev);
+isr_decl(i2c2_er);
+isr_decl(spi1);
+isr_decl(spi2);
+isr_decl(usart1);
+isr_decl(usart2);
+isr_decl(usart3);
+isr_decl(exti15_10);
+isr_decl(rtc_alarm);
+isr_decl(usb_wakeup);
+isr_decl(tim8_brk);
+isr_decl(tim8_up);
+isr_decl(tim8_trg_com);
+isr_decl(tim8_cc);
+isr_decl(adc3);
+isr_decl(fsmc);
+isr_decl(sdio);
+isr_decl(tim5);
+isr_decl(spi3);
+isr_decl(uart4);
+isr_decl(uart5);
+isr_decl(tim6);
+isr_decl(tim7);
+isr_decl(dma2_channel1);
+isr_decl(dma2_channel2);
+isr_decl(dma2_channel3);
+isr_decl(dma2_channel4_5);
+
+#undef isr_decl
+
+#define STM_ISR_WWDG_POS               0
+#define STM_ISR_PVD_POS                        1
+#define STM_ISR_TAMPER_STAMP_POS       2
+#define STM_ISR_RTC_WKUP_POS           3
+#define STM_ISR_FLASH_POS              4
+#define STM_ISR_RCC_POS                        5
+#define STM_ISR_EXTI0_POS              6
+#define STM_ISR_EXTI1_POS              7
+#define STM_ISR_EXTI2_POS              8
+#define STM_ISR_EXTI3_POS              9
+#define STM_ISR_EXTI4_POS              10
+#define STM_ISR_DMA1_CHANNEL1_POS      11
+#define STM_ISR_DMA1_CHANNEL2_POS      12
+#define STM_ISR_DMA1_CHANNEL3_POS      13
+#define STM_ISR_DMA1_CHANNEL4_POS      14
+#define STM_ISR_DMA1_CHANNEL5_POS      15
+#define STM_ISR_DMA1_CHANNEL6_POS      16
+#define STM_ISR_DMA1_CHANNEL7_POS      17
+#define STM_ISR_ADC1_2_POS             18
+#define STM_ISR_USB_HP_POS             19
+#define STM_ISR_USB_LP_POS             20
+#define STM_ISR_CAN_RX1_POS            21
+#define STM_ISR_CAN_SCE_POS            22
+#define STM_ISR_EXTI9_5_POS            23
+#define STM_ISR_TIM1_BRK_POS           24
+#define STM_ISR_TIM1_UP_POS            25
+#define STM_ISR_TIM1_TRG_COM_POS       26
+#define STM_ISR_TIM1_CC_POS            27
+#define STM_ISR_TIM2_POS               28
+#define STM_ISR_TIM3_POS               29
+#define STM_ISR_TIM4_POS               30
+#define STM_ISR_I2C1_EV_POS            31
+#define STM_ISR_I2C1_ER_POS            32
+#define STM_ISR_I2C2_EV_POS            33
+#define STM_ISR_I2C2_ER_POS            34
+#define STM_ISR_SPI1_POS               35
+#define STM_ISR_SPI2_POS               36
+#define STM_ISR_USART1_POS             37
+#define STM_ISR_USART2_POS             38
+#define STM_ISR_USART3_POS             39
+#define STM_ISR_EXTI15_10_POS          40
+#define STM_ISR_RTC_ALARM_POS          41
+#define STM_ISR_USB_WAKEUP_POS         42
+#define STM_ISR_TIM8_BRK_POS           43
+#define STM_ISR_TIM8_UP_POS            44
+#define STM_ISR_TIM8_TRG_COM_POS       45
+#define STM_ISR_TIM8_CC_POS            46
+#define STM_ISR_ADC3_POS               47
+#define STM_ISR_FSMC_POS               48
+#define STM_ISR_SDIO_POS               49
+#define STM_ISR_TIM5_POS               50
+#define STM_ISR_SPI3_POS               51
+#define STM_ISR_UART4_POS              52
+#define STM_ISR_UART5_POS              53
+#define STM_ISR_TIM6_POS               54
+#define STM_ISR_TIM7_POS               55
+#define STM_ISR_DMA2_CHANNEL1_POS      56
+#define STM_ISR_DMA2_CHANNEL2_POS      57
+#define STM_ISR_DMA2_CHANNEL3_POS      58
+#define STM_ISR_DMA3_CHANNEL4_5_POS    59
+
+#endif
diff --git a/src/stm32f103-nucleo/.gitignore b/src/stm32f103-nucleo/.gitignore
new file mode 100644 (file)
index 0000000..5c7fb91
--- /dev/null
@@ -0,0 +1,2 @@
+ao_product.h
+nucleo-*.elf
diff --git a/src/stm32f103-nucleo/Makefile b/src/stm32f103-nucleo/Makefile
new file mode 100644 (file)
index 0000000..72b84b6
--- /dev/null
@@ -0,0 +1,72 @@
+include ../stm32f1/Makefile.defs
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_exti.h \
+       ao_product.h \
+       ao_st7565.h \
+       stm32f1.h \
+       Makefile
+
+ALTOS_SRC = \
+       ao_clock.c \
+       ao_timer.c \
+       ao_mutex.c \
+       ao_boot_chain.c \
+       ao_interrupt.c \
+       ao_product.c \
+       ao_romconfig.c \
+       ao_led.c \
+       ao_task.c \
+       ao_panic.c \
+       ao_stdio.c \
+       ao_serial_stm.c \
+       ao_usb_stm.c \
+       ao_dma_stm.c \
+       ao_spi_stm.c \
+       ao_st7565.c \
+       ao_rect.c \
+       ao_text.c \
+       ao_box.c \
+       ao_copy.c \
+       ao_blt.c \
+       ao_line.c \
+       ao_logo.c \
+       ao_poly.c \
+       BitstreamVeraSans-Roman-58.c \
+       BitstreamVeraSans-Roman-24.c \
+       BitstreamVeraSans-Roman-10.c \
+       BitstreamVeraSans-Roman-12.c \
+       BenguiatGothicStd-Bold-26.c \
+       ao_cmd.c
+
+PRODUCT=Nucleo-f103
+PRODUCT_DEF=-DNUCLEO_F103
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(STM32F1_CFLAGS)
+
+PROGNAME=nucleo
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) hello.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ)
+       $(call quiet,CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/stm32f103-nucleo/ao_pins.h b/src/stm32f103-nucleo/ao_pins.h
new file mode 100644 (file)
index 0000000..636960f
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2023 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define AO_HSE         1
+#define AO_HSE_BYPASS  1
+
+#define AO_SYSCLK      72000000
+#define AO_HCLK                72000000
+#define AO_APB1CLK     36000000
+#define AO_APB2CLK     72000000
+#define AO_ADCCLK      12000000
+
+#define AO_RCC_CFGR_USBPRE     STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL     STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE   STM_RCC_CFGR_PLLXTPRE_1
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE1_DIV_2
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+#define AO_RCC_CFGR_ADCPRE     STM_RCC_CFGR_ADCPRE_6
+
+#define HAS_BEEP       0
+#define HAS_USB                1
+
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT     (&stm_gpiob)
+#define AO_USB_PULLUP_PIN      12
+
+#define HAS_LED                1
+#define LED_0_PORT     (&stm_gpioa)
+#define LED_0_PIN      5
+#define AO_LED_GREEN   (1 << 0)
+#define AO_LED_PANIC   AO_LED_GREEN
+
+#define HAS_SERIAL_1           0
+#define USE_SERIAL_1_STDIN     0
+#define SERIAL_1_PA9_PA10      1
+
+#define HAS_SERIAL_2           1
+#define USE_SERIAL_2_STDIN     1
+#define SERIAL_2_PA2_PA3       1
+#define SERIAL_2_SPEED         AO_SERIAL_SPEED_115200
+
+#define HAS_SPI_1              1
+#define SPI_1_PA5_PA6_PA7      1
+#define SPI_1_MODE_OUTPUT      STM_GPIO_CR_MODE_OUTPUT_10MHZ
+
+/* Chip Select. LCD pin 1. nucleo PA4 = A2  */
+#define AO_ST7565_CS_PORT      (&stm_gpioa)    /* pin 1 */
+#define AO_ST7565_CS_PIN       4
+
+/* Reset. LCD pin 2. nucleo PA0 = A0 */
+#define AO_ST7565_RESET_PORT   (&stm_gpioa)    /* pin 2 */
+#define AO_ST7565_RESET_PIN    0
+
+/* A0. LCD pin 3. nucleo PA1 = A1 */
+#define AO_ST7565_A0_PORT      (&stm_gpioa)    /* pin 3 */
+#define AO_ST7565_A0_PIN       1
+
+/* SCLK. LCD DB6 pin 12. nucleo PA5 = D13 */
+/* MOSI. LCD DB7 pin 13. nucleo PA7 = D11 */
+#define AO_ST7565_SPI_BUS      (AO_SPI_1_PA5_PA6_PA7 | AO_SPI_MODE_3)
+#define AO_ST7565_WIDTH                128
+#define AO_ST7565_HEIGHT       64
+#define AO_ST7565_BIAS         ST7565_LCD_BIAS_1_9
diff --git a/src/stm32f103-nucleo/flash-loader/Makefile b/src/stm32f103-nucleo/flash-loader/Makefile
new file mode 100644 (file)
index 0000000..8246b4d
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=nucleo-f103
+include $(TOPDIR)/stm32f1/Makefile-flash.defs
diff --git a/src/stm32f103-nucleo/flash-loader/ao_pins.h b/src/stm32f103-nucleo/flash-loader/ao_pins.h
new file mode 100644 (file)
index 0000000..0353aa8
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define AO_HSE         1
+#define AO_HSE_BYPASS  1
+
+#define AO_SYSCLK      72000000
+#define AO_HCLK                72000000
+#define AO_APB1CLK     36000000
+#define AO_APB2CLK     72000000
+#define AO_ADCCLK      12000000
+
+#define AO_RCC_CFGR_USBPRE     STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL     STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE   STM_RCC_CFGR_PLLXTPRE_1
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE1_DIV_2
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+#define AO_RCC_CFGR_ADCPRE     STM_RCC_CFGR_ADCPRE_6
+
+#include <ao_flash_stm_pins.h>
+
+/* USB DM is on PA11 (CN10 pin 14). USB DP is on PA12 (CN10 pin 12) */
+
+/* For pullup, we'll use PB6 (CN10 pin 16) */
+
+#define AO_BOOT_PIN                    1
+#define AO_BOOT_APPLICATION_GPIO       stm_gpiob
+#define AO_BOOT_APPLICATION_PIN                6
+#define AO_BOOT_APPLICATION_VALUE      1
+#define AO_BOOT_APPLICATION_MODE       AO_EXTI_MODE_PULL_UP
+
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT     (&stm_gpiob)
+#define AO_USB_PULLUP_PIN      12
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/stm32f103-nucleo/hello.c b/src/stm32f103-nucleo/hello.c
new file mode 100644 (file)
index 0000000..ed4e895
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright © 2023 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_st7565.h>
+
+#define WIDTH  AO_ST7565_WIDTH
+#define HEIGHT AO_ST7565_HEIGHT
+#define STRIDE AO_BITMAP_STRIDE(WIDTH)
+
+static uint32_t        image[STRIDE * HEIGHT];
+
+static struct ao_bitmap fb = {
+       .base = image,
+       .stride = STRIDE,
+       .width = WIDTH,
+       .height = HEIGHT,
+       .damage = AO_BOX_INIT,
+};
+
+static void
+ao_st7565_test(void)
+{
+       ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+       ao_st7565_update(&fb);
+       ao_text(&fb, &BitstreamVeraSans_Roman_24_font,
+               0, 20, "hello world", AO_BLACK, AO_COPY);
+       ao_st7565_update(&fb);
+}
+
+static int16_t x1 = 32, _y1 = 10, x2 = 32, y2 = 40;
+static int16_t dx1 = 2, dy1 = 2, dx2 = -2, dy2 = -1;
+
+#define bounds(v,m,M,d)        \
+               if (v < m) {                    \
+                       v = m + m - v;          \
+                       d = -d;                 \
+               } else if (v > M) {             \
+                       v = M - (v - M);        \
+                       d = -d;                 \
+               }
+
+static void
+ao_st7565_line(void)
+{
+       int     i;
+
+       for (i = 0; i < 100; i++) {
+               ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+               ao_line(&fb, x1, _y1, x2, y2, AO_BLACK, AO_COPY);
+               ao_st7565_update(&fb);
+               x1 += dx1;
+               _y1 += dy1;
+               x2 += dx2;
+               y2 += dy2;
+               printf("%d,%d - %d,%d\n", x1, _y1, x2, y2);
+               fflush(stdout);
+               bounds(x1, 0, WIDTH, dx1);
+               bounds(x2, 0, WIDTH, dx2);
+               bounds(_y1, 0, HEIGHT, dy1);
+               bounds(y2, 0, HEIGHT, dy2);
+               ao_delay(AO_MS_TO_TICKS(200));
+       }
+}
+
+static const float pad_volts = 12.3f;
+static const float lco_volts = 4.1f;
+static const int rssi = -30;
+
+static int     boxes[] = { 1, 2, 3, 5, 8, 11, 13, 17, 19, 23, 29, 31, 37, 62, 97 };
+
+//static int   max_box = 97;
+
+#define ARRAYSIZE(a)   (sizeof(a) / sizeof((a)[0]))
+
+static bool
+valid_box(int box)
+{
+       size_t i;
+       if (box == 0)
+               return true;
+       for (i = 0; i < ARRAYSIZE(boxes); i++)
+               if (boxes[i] == box)
+                       return true;
+       return false;
+}
+
+#if 0
+static void
+next_box(void)
+{
+       for (int n = box_number + 1; n <= max_box; n++)
+               if (valid_box(n)) {
+                       box_number = n;
+                       return;
+               }
+       box_number = 0;
+}
+
+static void
+prev_box(void)
+{
+       for (int n = box_number - 1; n >= 0; n--)
+               if (valid_box(n)) {
+                       box_number = n;
+                       return;
+               }
+       box_number = max_box;
+}
+#endif
+
+static const struct ao_transform logo_transform = {
+       .x_scale = 48, .x_off = 2,
+       .y_scale = 48, .y_off = 0,
+};
+
+#define BIG_FONT BitstreamVeraSans_Roman_58_font
+#define VOLT_FONT BitstreamVeraSans_Roman_58_font
+#define SMALL_FONT BitstreamVeraSans_Roman_12_font
+#define TINY_FONT BitstreamVeraSans_Roman_10_font
+#define LOGO_FONT BenguiatGothicStd_Bold_26_font
+
+#define LABEL_Y                (int16_t) (SMALL_FONT.ascent)
+#define VALUE_Y                (int16_t) (LABEL_Y + BIG_FONT.ascent + 5)
+#define BOX_X          2
+#define PAD_X          90
+#define BOX_LABEL_X    30
+#define VOLT_LABEL_X   25
+#define RSSI_LABEL_X   15
+#define PAD_LABEL_X    95
+#define SEP_X          (PAD_X - 8)
+#define SCAN_X         (WIDTH - 100) / 2
+#define SCAN_Y         50
+#define SCAN_HEIGHT    3
+#define FOUND_Y                63
+#define FOUND_X                6
+#define FOUND_WIDTH    17
+#define MAX_VALID      (WIDTH / FOUND_WIDTH)
+
+static int16_t box_number = 88;
+static int16_t pad_number = 8;
+
+static void
+ao_st7565_poly(void)
+{
+       int16_t scan_number;
+       char    str[8];
+       int     i;
+       int     v;
+       int     last_box;
+       int16_t b;
+
+       for (scan_number = 0; scan_number < 100; scan_number++) {
+               ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+               ao_logo(&fb, &logo_transform, &LOGO_FONT, AO_BLACK, AO_COPY);
+               if (scan_number) {
+                       ao_rect(&fb, SCAN_X, SCAN_Y, (int16_t) scan_number, SCAN_HEIGHT, AO_BLACK, AO_COPY);
+                       b = 0;
+                       v = 0;
+                       last_box = 0;
+                       for (i = scan_number; i > 1; i--) {
+                               if (valid_box(i)) {
+                                       if (!last_box)
+                                               last_box = i;
+                                       v++;
+                                       if (v == MAX_VALID)
+                                               break;
+                               }
+                       }
+                       for (; i <= scan_number; i++) {
+                               if (valid_box(i)) {
+                                       sprintf(str, "%02d%s", i, i == last_box ? "" : ",");
+                                       ao_text(&fb, &TINY_FONT, (int16_t) (FOUND_X + FOUND_WIDTH * b),
+                                               FOUND_Y, str, AO_BLACK, AO_COPY);
+                                       b++;
+                               }
+                       }
+               }
+               ao_st7565_update(&fb);
+               ao_delay(AO_MS_TO_TICKS(50));
+       }
+       ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+       switch (box_number) {
+       case 0:
+               sprintf(str, "%4.1f", lco_volts);
+               ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+               ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, "LCO Battery", AO_BLACK, AO_COPY);
+               break;
+       default:
+               switch (pad_number) {
+               case -1:
+                       sprintf(str, "%4.1f", pad_volts);
+                       ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+                       ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, "Pad Battery", AO_BLACK, AO_COPY);
+                       break;
+               case 0:
+                       sprintf(str, "%4d", rssi);
+                       ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+                       ao_text(&fb, &SMALL_FONT, RSSI_LABEL_X, LABEL_Y, "Signal Strength", AO_BLACK, AO_COPY);
+                       break;
+               default:
+                       sprintf(str, "%02d", box_number);
+                       ao_text(&fb, &BIG_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+                       ao_text(&fb, &SMALL_FONT, BOX_LABEL_X, LABEL_Y, "Box", AO_BLACK, AO_COPY);
+
+                       sprintf(str, "%d", pad_number);
+                       ao_text(&fb, &BIG_FONT, PAD_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+                       ao_text(&fb, &SMALL_FONT, PAD_LABEL_X, LABEL_Y, "Pad", AO_BLACK, AO_COPY);
+
+                       ao_rect(&fb, SEP_X, 0, 2, HEIGHT, AO_BLACK, AO_COPY);
+               }
+               break;
+       }
+       ao_st7565_update(&fb);
+}
+
+const struct ao_cmds ao_st7565_cmds[] = {
+       { ao_st7565_test, "g\0Test ST7565 display" },
+       { ao_st7565_line, "l\0Draw lines" },
+       { ao_st7565_poly, "p\0Draw polygon" },
+       { 0, NULL },
+};
+
+int main(void)
+{
+       ao_clock_init();
+       ao_led_init();
+       ao_timer_init();
+       ao_task_init();
+       ao_dma_init();
+       ao_spi_init();
+       ao_serial_init();
+       ao_usb_init();
+       ao_st7565_init();
+       ao_cmd_init();
+       ao_cmd_register(ao_st7565_cmds);
+       ao_start_scheduler();
+}
diff --git a/src/stm32f103-nucleo/hello.ld b/src/stm32f103-nucleo/hello.ld
new file mode 100644 (file)
index 0000000..cc57e00
--- /dev/null
@@ -0,0 +1,7 @@
+__flash = 0x20000000;
+__flash_size = 4k;
+__ram   = 0x20001000;
+__ram_size = 4k;
+__stack_size = 512;
+
+INCLUDE picolibc.ld
index 385fe0e32b192d6c815ec0fd32bdb96f8d69abe0..5f0f07dea9ed0d7a6ce3ce2a67170beebf55c7d9 100644 (file)
@@ -36,8 +36,9 @@
 /* Companion bus wants something no faster than 200kHz */
 
 static inline uint32_t
-ao_spi_speed(uint32_t hz)
+ao_spi_speed(int index, uint32_t hz)
 {
+       (void) index;
        if (hz >= 4000000) return _AO_SPI_SPEED_4MHz;
        if (hz >= 2000000) return _AO_SPI_SPEED_2MHz;
        if (hz >= 1000000) return _AO_SPI_SPEED_1MHz;
index 1a9f28e30ef0b68d4f7ac69fe846e16dea915bb5..953a0c00c2a69ad107124a91b337bb04ed05256e 100644 (file)
@@ -36,8 +36,9 @@
 #define _AO_SPI_SPEED_187500Hz STM_SPI_CR1_BR_PCLK_256
 
 static inline uint32_t
-ao_spi_speed(uint32_t hz)
+ao_spi_speed(int index, uint32_t hz)
 {
+       (void) index;
        if (hz >=24000000) return _AO_SPI_SPEED_24MHz;
        if (hz >=12000000) return _AO_SPI_SPEED_12MHz;
        if (hz >= 6000000) return _AO_SPI_SPEED_6MHz;
index b434e7cb4b374aac2fb79ff90f13cc63ee31673e..b5bb8703c2cf10ca36c87b538a1a9072e4aa2326 100644 (file)
@@ -324,4 +324,6 @@ struct ao_adc {
  */
 #define AO_ADC_REFERENCE_DV    33
 
+#define AO_LCO_DRAG_RACE_BOX   1
+
 #endif /* _AO_PINS_H_ */
index 3d06a647246d4f6523195d94c447fadeede5687a..ed4b585910968a86bb5fc209db6a9eb8a3be0e12 100644 (file)
 
 #define AO_BUTTON_FIRE         1
 
+#define AO_LCO_DRAG_RACE_BOX   1
+
 #endif /* _AO_PINS_H_ */
index de89d746a41bb55e35c036e3cba74d91c9e9cf07..71049dd7dfea1af4e172ef1b8b43dd8fb409d175 100644 (file)
 
 #define AO_BUTTON_FIRE         1
 
+#define AO_LCO_DRAG_RACE_BOX   1
+
 #endif /* _AO_PINS_H_ */
index 78849bc9b43bc00f551a70133aef6fbdcd66327a..5f7352d7d69aea4984aa768828259439d9470517 100644 (file)
@@ -42,7 +42,7 @@ static uint8_t        ao_lco_event_debug;
 static uint8_t ao_lco_display_mutex;
 
 void
-ao_lco_show_pad(uint8_t pad)
+ao_lco_show_pad(int8_t pad)
 {
        ao_mutex_get(&ao_lco_display_mutex);
        ao_seven_segment_set(AO_LCO_PAD_DIGIT, (uint8_t) (pad | (ao_lco_drag_race << 4)));
@@ -67,7 +67,7 @@ ao_lco_show_pad(uint8_t pad)
                                 (0 << 6))
 
 void
-ao_lco_show_box(uint16_t box)
+ao_lco_show_box(int16_t box)
 {
        ao_mutex_get(&ao_lco_display_mutex);
        ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, (uint8_t) (box % 10 | (ao_lco_drag_race << 4)));
@@ -76,38 +76,69 @@ ao_lco_show_box(uint16_t box)
 }
 
 static void
-ao_lco_show_voltage(uint16_t decivolts)
+ao_lco_show_value(uint16_t value, uint8_t point)
 {
-       uint8_t tens, ones, tenths;
-
-       PRINTD("voltage %d\n", decivolts);
-       tenths = (uint8_t) (decivolts % 10);
-       ones = (uint8_t) ((decivolts / 10) % 10);
-       tens = (uint8_t) ((decivolts / 100) % 10);
+       uint8_t hundreds, tens, ones;
+
+       PRINTD("value %d\n", value);
+       ones = (uint8_t) (value % 10);
+       tens = (uint8_t) ((value / 10) % 10);
+       hundreds = (uint8_t) ((value / 100) % 10);
+       switch (point) {
+       case 2:
+               hundreds |= 0x10;
+               break;
+       case 1:
+               tens |= 0x10;
+               break;
+       case 0:
+               ones |= 0x10;
+               break;
+       default:
+               break;
+       }
        ao_mutex_get(&ao_lco_display_mutex);
-       ao_seven_segment_set(AO_LCO_PAD_DIGIT, tenths);
-       ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, ones | 0x10);
-       ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, tens);
+       ao_seven_segment_set(AO_LCO_PAD_DIGIT, ones);
+       ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, tens);
+       ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, hundreds);
        ao_mutex_put(&ao_lco_display_mutex);
 }
 
-void
-ao_lco_show(void)
+static void
+ao_lco_show_lco_voltage(void)
 {
-       if (ao_lco_pad == AO_LCO_PAD_VOLTAGE) {
-               ao_lco_show_voltage(ao_pad_query.battery);
-       } else {
-               ao_lco_show_pad(ao_lco_pad);
-               ao_lco_show_box(ao_lco_box);
-       }
+       struct ao_adc   packet;
+       int16_t         decivolt;
+
+       ao_adc_single_get(&packet);
+       decivolt = ao_battery_decivolt(packet.v_batt);
+       ao_lco_show_value((uint16_t) decivolt, 1);
 }
 
-uint8_t
-ao_lco_box_present(uint16_t box)
+void
+ao_lco_show(void)
 {
-       if (box >= AO_PAD_MAX_BOXES)
-               return 0;
-       return (ao_lco_box_mask[AO_LCO_MASK_ID(box)] >> AO_LCO_MASK_SHIFT(box)) & 1;
+       switch (ao_lco_box) {
+       case AO_LCO_LCO_VOLTAGE:
+               ao_lco_show_lco_voltage();
+               break;
+       default:
+               switch (ao_lco_pad) {
+               case AO_LCO_PAD_VOLTAGE:
+                       ao_lco_show_value(ao_pad_query.battery, 1);
+                       break;
+               case AO_LCO_PAD_RSSI:
+                       if (!(ao_lco_valid[ao_lco_box] & AO_LCO_VALID_LAST))
+                               ao_lco_show_value(888, 0);
+                       else
+                               ao_lco_show_value((uint16_t) (-ao_radio_cmac_rssi), 0);
+                       break;
+               default:
+                       ao_lco_show_pad(ao_lco_pad);
+                       ao_lco_show_box(ao_lco_box);
+                       break;
+               }
+       }
 }
 
 static void
@@ -132,23 +163,6 @@ ao_lco_set_select(void)
        }
 }
 
-static void
-ao_lco_step_box(int8_t dir)
-{
-       int32_t new_box = (int32_t) ao_lco_box;
-
-       do {
-               new_box += dir;
-               if (new_box > ao_lco_max_box)
-                       new_box = ao_lco_min_box;
-               else if (new_box < ao_lco_min_box)
-                       new_box = ao_lco_max_box;
-               if (new_box == ao_lco_box)
-                       break;
-       } while (!ao_lco_box_present((uint16_t) new_box));
-       ao_lco_set_box((uint16_t) new_box);
-}
-
 static struct ao_task  ao_lco_drag_task;
 
 static void
@@ -254,12 +268,7 @@ ao_lco_display_test(void)
 static void
 ao_lco_batt_voltage(void)
 {
-       struct ao_adc   packet;
-       int16_t         decivolt;
-
-       ao_adc_single_get(&packet);
-       decivolt = ao_battery_decivolt(packet.v_batt);
-       ao_lco_show_voltage((uint16_t) decivolt);
+       ao_lco_show_lco_voltage();
        ao_delay(AO_MS_TO_TICKS(1000));
 }
 
diff --git a/src/telelco-v3.0/.gitignore b/src/telelco-v3.0/.gitignore
new file mode 100644 (file)
index 0000000..a32ec26
--- /dev/null
@@ -0,0 +1,2 @@
+ao_product.h
+telelco*.elf
diff --git a/src/telelco-v3.0/Makefile b/src/telelco-v3.0/Makefile
new file mode 100644 (file)
index 0000000..cc17a22
--- /dev/null
@@ -0,0 +1,129 @@
+#
+# AltOS build for TeleLCO v3.0
+#
+#
+
+STM32F1_LINKER_SCRIPT=altos-128.ld
+
+include ../stm32f1/Makefile.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_boot.h \
+       ao_companion.h \
+       ao_data.h \
+       ao_sample.h \
+       ao_pins.h \
+       ao_product.h \
+       ao_lco.h \
+       ao_lco_cmd.h \
+       ao_lco_func.h \
+       ao_radio_spi.h \
+       ao_radio_cmac.h \
+       ao_cc1200_CC1200.h \
+       ao_cc1200.h \
+       ao_st7565.h \
+       ao_font.h \
+       ao_logo.h \
+       stm32f1.h
+
+#
+# Common AltOS sources
+#
+
+ALTOS_SRC = \
+       ao_boot_chain.c \
+       ao_interrupt.c \
+       ao_product.c \
+       ao_romconfig.c \
+       ao_cmd.c \
+       ao_config.c \
+       ao_task.c \
+       ao_led.c \
+       ao_stdio.c \
+       ao_panic.c \
+       ao_clock.c \
+       ao_timer.c \
+       ao_mutex.c \
+       ao_freq.c \
+       ao_adc_single_stm.c \
+       ao_dma_stm.c \
+       ao_spi_stm.c \
+       ao_beep_stm.c \
+       ao_convert_volt.c \
+       ao_fast_timer.c \
+       ao_pwm_stm.c \
+       ao_eeprom.c \
+       ao_flash_stm.c \
+       ao_usb_stm.c \
+       ao_exti_stm.c \
+       ao_cc1200.c \
+       ao_radio_cmac.c \
+       ao_aes.c \
+       ao_aes_tables.c \
+       ao_st7565.c \
+       ao_rect.c \
+       ao_text.c \
+       ao_text_width.c \
+       ao_box.c \
+       ao_copy.c \
+       ao_blt.c \
+       ao_line.c \
+       ao_logo.c \
+       ao_poly.c \
+       BitstreamVeraSans-Roman-58.c \
+       BitstreamVeraSans-Roman-24.c \
+       BitstreamVeraSans-Roman-10.c \
+       BitstreamVeraSans-Roman-12.c \
+       BenguiatGothicStd-Bold-26.c \
+       ao_quadrature.c \
+       ao_button.c \
+       ao_event.c \
+       ao_lco_bits.c \
+       ao_lco_v3.c \
+       ao_lco_cmd.c \
+       ao_lco_func.c \
+       ao_radio_cmac_cmd.c
+
+PRODUCT=TeleLCO-v3.0
+PRODUCT_DEF=-DTELELCO
+IDPRODUCT=0x0023
+
+CFLAGS = $(PRODUCT_DEF) $(STM32F1_CFLAGS)
+
+PROGNAME=telelco-v3.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_telelco.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos-128.ld
+       $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
+       rm -f ao_product.h
+
+ao_font.h:
+       cd ../draw && make ao_font.h ao_logo.h
+
+ao_logo.h: ao_font.h
+
+BitstreamVeraSans-Roman-58.c: ao_font.h
+BitstreamVeraSans-Roman-24.c: ao_font.h
+BitstreamVeraSans-Roman-10.c: ao_font.h
+BitstreamVeraSans-Roman-12.c: ao_font.h
+BenguiatGothicStd-Bold-26.c: ao_font.h
+
+install:
+
+uninstall:
diff --git a/src/telelco-v3.0/ao_lco_v3.c b/src/telelco-v3.0/ao_lco_v3.c
new file mode 100644 (file)
index 0000000..dc6b030
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_lco.h>
+#include <ao_event.h>
+#include <ao_quadrature.h>
+#include <ao_radio_cmac.h>
+#include <ao_st7565.h>
+#include <ao_adc_single.h>
+#include <ao_pwm.h>
+
+#define WIDTH  AO_ST7565_WIDTH
+#define HEIGHT AO_ST7565_HEIGHT
+#define STRIDE AO_BITMAP_STRIDE(WIDTH)
+
+static uint32_t        image[STRIDE * HEIGHT];
+
+static struct ao_bitmap fb = {
+       .base = image,
+       .stride = STRIDE,
+       .width = WIDTH,
+       .height = HEIGHT,
+       .damage = AO_BOX_INIT,
+};
+
+static const struct ao_transform logo_transform = {
+       .x_scale = 48, .x_off = 2,
+       .y_scale = 48, .y_off = 0,
+};
+
+static const struct ao_transform show_transform = {
+       .x_scale = 36, .x_off = 100,
+       .y_scale = 36, .y_off = 0,
+};
+
+#define BIG_FONT BitstreamVeraSans_Roman_58_font
+#define VOLT_FONT BitstreamVeraSans_Roman_58_font
+#define SMALL_FONT BitstreamVeraSans_Roman_12_font
+#define TINY_FONT BitstreamVeraSans_Roman_10_font
+#define LOGO_FONT BenguiatGothicStd_Bold_26_font
+
+#define LABEL_Y                (int16_t) (SMALL_FONT.ascent)
+#define VALUE_Y                (int16_t) (LABEL_Y + 5 + BIG_FONT.ascent)
+
+#define SEP_X          82
+#define SEP_WIDTH      2
+
+#define BOX_X          (SEP_X / 2)
+#define PAD_X          ((WIDTH + SEP_X + SEP_WIDTH) / 2)
+
+#define VALUE_LABEL_X  64
+#define RSSI_LABEL_X   15
+
+#define SCAN_X         (WIDTH - 100) / 2
+#define SCAN_Y         50
+#define SCAN_HEIGHT    3
+#define FOUND_Y                63
+#define FOUND_X                3
+#define FOUND_WIDTH    (WIDTH - 6)
+#define CONTRAST_LABEL_X       37
+#define CONTRAST_WIDTH 100
+#define CONTRAST_X     (WIDTH - CONTRAST_WIDTH) / 2
+#define CONTRAST_Y     20
+#define CONTRAST_HEIGHT        20
+#define CONTRAST_VALUE_X       64
+#define CONTRAST_VALUE_Y       (CONTRAST_Y + CONTRAST_HEIGHT + SMALL_FONT.ascent + 3)
+#define BACKLIGHT_LABEL_X      37
+#define BACKLIGHT_WIDTH        100
+#define BACKLIGHT_X    (WIDTH - BACKLIGHT_WIDTH) / 2
+#define BACKLIGHT_Y    20
+#define BACKLIGHT_HEIGHT       20
+#define BACKLIGHT_VALUE_X      64
+#define BACKLIGHT_VALUE_Y      (BACKLIGHT_Y + BACKLIGHT_HEIGHT + SMALL_FONT.ascent + 3)
+#define INFO_START_Y   ((int16_t) (SMALL_FONT.ascent + 2))
+#define INFO_STEP_Y    ((int16_t) (SMALL_FONT.ascent + 3))
+
+#define AO_LCO_DRAG_RACE_START_TIME    AO_SEC_TO_TICKS(5)
+#define AO_LCO_DRAG_RACE_STOP_TIME     AO_SEC_TO_TICKS(2)
+
+/* UI values */
+static uint8_t ao_lco_select_mode;
+static uint8_t ao_lco_event_debug;
+
+#define PRINTE(...) do { if (!ao_lco_debug && !ao_lco_event_debug) break; printf ("\r%5lu %s: ", (unsigned long) ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
+#define AO_LCO_SELECT_BOX      0
+#define AO_LCO_SELECT_PAD      1
+
+static uint8_t ao_lco_display_mutex;
+
+static void
+_ao_center_text(int16_t x, int16_t y, const struct ao_font *font, const char *str)
+{
+       int16_t width = ao_text_width(font, str);
+       ao_text(&fb, font, x - width/2, y, str, AO_BLACK, AO_COPY);
+}
+
+static void
+_ao_lco_show_pad(int8_t pad)
+{
+       char    str[5];
+
+       _ao_center_text(PAD_X, LABEL_Y, &SMALL_FONT, "Pad");
+       snprintf(str, sizeof(str), "%d", pad);
+       _ao_center_text(PAD_X, VALUE_Y, &BIG_FONT, str);
+}
+
+static void
+_ao_lco_show_box(int16_t box)
+{
+       char    str[7];
+
+       _ao_center_text(BOX_X, LABEL_Y, &SMALL_FONT, "Bank");
+       snprintf(str, sizeof(str), "%d", box);
+       _ao_center_text(BOX_X, VALUE_Y, &BIG_FONT, str);
+}
+
+static void
+_ao_lco_show_voltage(uint16_t decivolts, const char *label)
+{
+       char    str[7];
+
+       PRINTD("voltage %d\n", decivolts);
+       _ao_center_text(WIDTH/2, LABEL_Y, &SMALL_FONT, label);
+       snprintf(str, sizeof(str), "%d.%d", decivolts / 10, decivolts % 10);
+       _ao_center_text(WIDTH/2, VALUE_Y, &BIG_FONT, str);
+}
+
+static void
+_ao_lco_batt_voltage(void)
+{
+       struct ao_adc   packet;
+       int16_t         decivolt;
+
+       ao_adc_single_get(&packet);
+       decivolt = ao_battery_decivolt(packet.v_batt);
+       _ao_lco_show_voltage((uint16_t) decivolt, "LCO Battery");
+       ao_st7565_update(&fb);
+}
+
+static void
+_ao_lco_show_contrast(void)
+{
+       char buf[8];
+       uint8_t brightness = ao_st7565_get_brightness();
+       int16_t contrast = (int16_t) (brightness * CONTRAST_WIDTH / AO_LCO_MAX_CONTRAST);
+
+       _ao_center_text(WIDTH/2, LABEL_Y, &SMALL_FONT, "Contrast");
+       ao_rect(&fb, CONTRAST_X, CONTRAST_Y, contrast, CONTRAST_HEIGHT, AO_BLACK, AO_COPY);
+       snprintf(buf, sizeof(buf), "%d %%", brightness * 100 / AO_LCO_MAX_CONTRAST);
+       _ao_center_text(WIDTH/2, CONTRAST_VALUE_Y, &SMALL_FONT, buf);
+}
+
+static void
+_ao_lco_show_backlight(void)
+{
+       char buf[8];
+       int32_t backlight = ao_lco_get_backlight();
+       int16_t value = (int16_t) (backlight * BACKLIGHT_WIDTH / AO_LCO_MAX_BACKLIGHT);
+
+       _ao_center_text(WIDTH/2, LABEL_Y, &SMALL_FONT, "Backlight");
+       ao_rect(&fb, BACKLIGHT_X, BACKLIGHT_Y, value, BACKLIGHT_HEIGHT, AO_BLACK, AO_COPY);
+       snprintf(buf, sizeof(buf), "%ld %%", backlight * 100 / AO_LCO_MAX_BACKLIGHT);
+       _ao_center_text(WIDTH/2, BACKLIGHT_VALUE_Y, &SMALL_FONT, buf);
+}
+
+static int16_t info_y;
+
+static void
+_ao_lco_info(const char *format, ...)
+{
+       va_list a;
+       char    buf[20];
+       va_start(a, format);
+       vsnprintf(buf, sizeof(buf), format, a);
+       va_end(a);
+       ao_text(&fb, &SMALL_FONT, 0, info_y, buf, AO_BLACK, AO_COPY);
+       info_y += INFO_STEP_Y;
+}
+
+static void
+_ao_lco_show_info(void)
+{
+       info_y = INFO_START_Y;
+       ao_logo_poly(&fb, &show_transform, AO_BLACK, AO_COPY);
+       _ao_lco_info("%s", ao_product);
+       _ao_lco_info("Version: %s", ao_version);
+       _ao_lco_info("Serial: %d", ao_serial_number);
+       _ao_lco_info("Callsign: %s", ao_config.callsign);
+       _ao_lco_info("Frequency: %ld.%03d",
+                    ao_config.frequency / 1000,
+                    (int) (ao_config.frequency % 1000));
+}
+
+static void
+_ao_lco_show_rssi(void)
+{
+       char label[20];
+       int16_t width;
+       snprintf(label, sizeof(label), "Bank %d RSSI", ao_lco_box);
+       width = ao_text_width(&SMALL_FONT, label);
+       ao_text(&fb, &SMALL_FONT, VALUE_LABEL_X - width / 2, LABEL_Y, label, AO_BLACK, AO_COPY);
+       if (!(ao_lco_valid[ao_lco_box] & AO_LCO_VALID_LAST))
+               strcpy(label, "---");
+       else
+               snprintf(label, sizeof(label), "%d", ao_radio_cmac_rssi);
+       width = ao_text_width(&VOLT_FONT, label);
+       ao_text(&fb, &VOLT_FONT, VALUE_LABEL_X - width / 2, VALUE_Y, label, AO_BLACK, AO_COPY);
+}
+
+static void
+_ao_lco_show_pad_battery(void)
+{
+       char label[20];
+       snprintf(label, sizeof(label), "Bank %d Battery", ao_lco_box);
+       _ao_lco_show_voltage(ao_pad_query.battery, label);
+}
+
+void
+ao_lco_show(void)
+{
+       ao_mutex_get(&ao_lco_display_mutex);
+       ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+       switch (ao_lco_box) {
+       case AO_LCO_LCO_VOLTAGE:
+               _ao_lco_batt_voltage();
+               break;
+       case AO_LCO_CONTRAST:
+               _ao_lco_show_contrast();
+               break;
+       case AO_LCO_BACKLIGHT:
+               _ao_lco_show_backlight();
+               break;
+       case AO_LCO_INFO:
+               _ao_lco_show_info();
+               break;
+       default:
+               switch (ao_lco_pad) {
+               case AO_LCO_PAD_RSSI:
+                       _ao_lco_show_rssi();
+                       break;
+               case AO_LCO_PAD_VOLTAGE:
+                       _ao_lco_show_pad_battery();
+                       break;
+               default:
+                       _ao_lco_show_pad(ao_lco_pad);
+                       _ao_lco_show_box(ao_lco_box);
+                       ao_rect(&fb, SEP_X, 0, SEP_WIDTH, HEIGHT, AO_BLACK, AO_COPY);
+               }
+               break;
+       }
+       ao_st7565_update(&fb);
+       ao_mutex_put(&ao_lco_display_mutex);
+}
+
+static void
+ao_lco_set_select(void)
+{
+       if (ao_lco_armed) {
+               ao_led_off(AO_LED_PAD);
+               ao_led_off(AO_LED_BOX);
+       } else {
+               switch (ao_lco_select_mode) {
+               case AO_LCO_SELECT_PAD:
+                       ao_led_off(AO_LED_BOX);
+                       ao_led_on(AO_LED_PAD);
+                       break;
+               case AO_LCO_SELECT_BOX:
+                       ao_led_off(AO_LED_PAD);
+                       ao_led_on(AO_LED_BOX);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+
+void
+ao_lco_set_contrast(int32_t contrast)
+{
+       ao_st7565_set_brightness((uint8_t) contrast);
+}
+
+int32_t
+ao_lco_get_contrast(void)
+{
+       return (int32_t) ao_st7565_get_brightness();
+}
+
+static uint16_t ao_backlight;
+
+void
+ao_lco_set_backlight(int32_t backlight)
+{
+       ao_backlight = (uint16_t) backlight;
+       ao_pwm_set(AO_LCD_BL_PWM_CHAN, ao_backlight);
+}
+
+int32_t
+ao_lco_get_backlight(void)
+{
+       return (int32_t) ao_backlight;
+}
+
+static struct ao_task  ao_lco_drag_task;
+
+static void
+ao_lco_drag_monitor(void)
+{
+       AO_TICK_TYPE    delay = ~0UL;
+       AO_TICK_TYPE    now;
+
+       ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+       for (;;) {
+               PRINTD("Drag monitor count %d delay %lu\n", ao_lco_drag_beep_count, (unsigned long) delay);
+               if (delay == (AO_TICK_TYPE) ~0)
+                       ao_sleep(&ao_lco_drag_beep_count);
+               else
+                       ao_sleep_for(&ao_lco_drag_beep_count, delay);
+
+               delay = ~0UL;
+               now = ao_time();
+               delay = ao_lco_drag_warn_check(now, delay);
+               delay = ao_lco_drag_beep_check(now, delay);
+       }
+}
+
+static void
+ao_lco_input(void)
+{
+       static struct ao_event  event;
+
+       for (;;) {
+               ao_event_get(&event);
+               PRINTE("event type %d unit %d value %ld\n",
+                      event.type, event.unit, (long) event.value);
+               switch (event.type) {
+               case AO_EVENT_QUADRATURE:
+                       switch (event.unit) {
+                       case AO_QUADRATURE_SELECT:
+                               if (!ao_lco_armed) {
+                                       switch (ao_lco_select_mode) {
+                                       case AO_LCO_SELECT_PAD:
+                                               ao_lco_step_pad((int8_t) event.value);
+                                               break;
+                                       case AO_LCO_SELECT_BOX:
+                                               ao_lco_step_box((int8_t) event.value);
+                                               break;
+                                       default:
+                                               break;
+                                       }
+                               }
+                               break;
+                       }
+                       break;
+               case AO_EVENT_BUTTON:
+                       switch (event.unit) {
+                       case AO_BUTTON_ARM:
+                               ao_lco_set_armed((uint8_t) event.value);
+                               ao_lco_set_select();
+                               break;
+                       case AO_BUTTON_FIRE:
+                               if (ao_lco_armed)
+                                       ao_lco_set_firing((uint8_t) event.value);
+                               break;
+                       case AO_BUTTON_DRAG_SELECT:
+                               if (event.value)
+                                       ao_lco_toggle_drag();
+                               break;
+                       case AO_BUTTON_DRAG_MODE:
+                               if (event.value)
+                                       ao_lco_drag_enable();
+                               else
+                                       ao_lco_drag_disable();
+                               break;
+                       case AO_BUTTON_ENCODER_SELECT:
+                               if (event.value) {
+                                       if (!ao_lco_armed) {
+                                               ao_lco_select_mode = 1 - ao_lco_select_mode;
+                                               ao_lco_set_select();
+                                       }
+                               }
+                               break;
+                       }
+                       break;
+               }
+       }
+}
+
+/*
+ * Light up everything for a second at power on to let the user
+ * visually inspect the system for correct operation
+ */
+static void
+ao_lco_display_test(void)
+{
+       ao_led_on(AO_LEDS_AVAILABLE);
+       ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_BLACK, AO_COPY);
+       ao_st7565_update(&fb);
+       ao_delay(AO_MS_TO_TICKS(250));
+       ao_led_off(AO_LEDS_AVAILABLE);
+}
+
+static struct ao_task ao_lco_input_task;
+static struct ao_task ao_lco_monitor_task;
+static struct ao_task ao_lco_arm_warn_task;
+static struct ao_task ao_lco_igniter_status_task;
+
+static int16_t found_width;
+#define MAX_FOUND      32
+static int16_t found_boxes[MAX_FOUND];
+static uint8_t nfound;
+
+void
+ao_lco_search_start(void)
+{
+       ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+       ao_logo(&fb, &logo_transform, &LOGO_FONT, AO_BLACK, AO_COPY);
+       found_width = 0;
+       nfound = 0;
+}
+
+void
+ao_lco_search_box_check(int16_t box)
+{
+       if (box > 0)
+               ao_rect(&fb, SCAN_X, SCAN_Y, box, SCAN_HEIGHT, AO_BLACK, AO_COPY);
+       ao_st7565_update(&fb);
+}
+
+void
+ao_lco_search_box_present(int16_t box)
+{
+       char    str[8];
+       int16_t width;
+       int16_t box_top = FOUND_Y - TINY_FONT.ascent;
+       int16_t x;
+       uint8_t n;
+
+       snprintf(str, sizeof(str), "%s%u", nfound ? ", " : "", box);
+       width = ao_text_width(&TINY_FONT, str);
+       while (found_width + width > FOUND_WIDTH || nfound == MAX_FOUND)
+       {
+               snprintf(str, sizeof(str), "%u, ", found_boxes[0]);
+               found_width -= ao_text_width(&TINY_FONT, str);
+               memmove(&found_boxes[0], &found_boxes[1], (nfound - 1) * sizeof (int16_t));
+               nfound--;
+       }
+       found_boxes[nfound++] = box;
+
+       ao_rect(&fb, FOUND_X, FOUND_Y - TINY_FONT.ascent, FOUND_WIDTH, HEIGHT - box_top, AO_WHITE, AO_COPY);
+       x = FOUND_X;
+       for (n = 0; n < nfound; n++) {
+               snprintf(str, sizeof(str), "%s%u", n ? ", " : "", found_boxes[n]);
+               int16_t next_x = ao_text(&fb, &TINY_FONT, x, FOUND_Y, str, AO_BLACK, AO_COPY);
+               x = next_x;
+       }
+       found_width = x - FOUND_X;
+}
+
+void
+ao_lco_search_done(void)
+{
+       ao_st7565_update(&fb);
+}
+
+static void
+ao_lco_main(void)
+{
+       ao_lco_display_test();
+       ao_lco_search();
+       ao_add_task(&ao_lco_input_task, ao_lco_input, "lco input");
+       ao_add_task(&ao_lco_arm_warn_task, ao_lco_arm_warn, "lco arm warn");
+       ao_add_task(&ao_lco_igniter_status_task, ao_lco_igniter_status, "lco igniter status");
+       ao_add_task(&ao_lco_drag_task, ao_lco_drag_monitor, "drag race");
+       ao_lco_monitor();
+}
+
+#if DEBUG
+static void
+ao_lco_set_debug(void)
+{
+       uint32_t r = ao_cmd_decimal();
+       if (ao_cmd_status == ao_cmd_success){
+               ao_lco_debug = r & 1;
+               ao_lco_event_debug = (r & 2) >> 1;
+       }
+}
+
+const struct ao_cmds ao_lco_cmds[] = {
+       { ao_lco_set_debug,     "D <0 off, 1 on>\0Debug" },
+       { ao_lco_search,        "s\0Search for pad boxes" },
+       { ao_lco_pretend,       "p\0Pretend there are lots of pad boxes" },
+       { 0, NULL }
+};
+#endif
+
+void
+ao_lco_init(void)
+{
+       ao_add_task(&ao_lco_monitor_task, ao_lco_main, "lco monitor");
+#if DEBUG
+       ao_cmd_register(&ao_lco_cmds[0]);
+#endif
+}
diff --git a/src/telelco-v3.0/ao_pins.h b/src/telelco-v3.0/ao_pins.h
new file mode 100644 (file)
index 0000000..e286cae
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* 16MHz crystal */
+
+#define AO_HSE         1
+#define AO_HSE_BYPASS  0
+
+#define AO_SYSCLK      72000000
+#define AO_HCLK                72000000
+#define AO_APB1CLK     36000000
+#define AO_APB2CLK     72000000
+#define AO_ADCCLK      12000000
+
+/* PLLMUL is 9, PLLXTPRE (pre divider) is 2, so the
+ * overall PLLCLK is 16 * 9/2 = 72MHz (used as SYSCLK)
+ *
+ * HCLK is SYSCLK / 1 (HPRE_DIV) = 72MHz (72MHz max)
+ * USB is PLLCLK / 1.5 (USBPRE)= 48MHz (must be 48MHz)
+ * APB2 is HCLK / 1 (PPRE2_DIV) = 72MHz (72MHz max)
+ * APB1 is HCLK / 2 (PPRE1_DIV) = 36MHz (36MHz max)
+ * ADC is APB2 / 6 (ADCPRE) = 12MHz (14MHz max)
+ */
+
+#define AO_RCC_CFGR_USBPRE     STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL     STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE   STM_RCC_CFGR_PLLXTPRE_2
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE1_DIV_2
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+#define AO_RCC_CFGR_ADCPRE     STM_RCC_CFGR_ADCPRE_6
+
+
+#define AO_CC1200_FOSC         40000000
+
+#define HAS_EEPROM             1
+#define USE_INTERNAL_FLASH     1
+#define USE_EEPROM_CONFIG      1
+#define USE_STORAGE_CONFIG     0
+#define HAS_USB                        1
+#define HAS_BEEP               1
+#define BEEPER_TIMER           3
+#define BEEPER_CHANNEL         1
+#define BEEPER_PORT            (&stm_gpioc)
+#define BEEPER_PIN             6
+#define AO_BEEP_MID_DEFAULT    179     /* 2100 Hz */
+#define HAS_RADIO              1
+#define HAS_RADIO_RATE         1
+#define HAS_TELEMETRY          0
+#define HAS_AES                        1
+#define HAS_STATIC_TEST                0
+
+#define HAS_USB_PULLUP         1
+#define AO_USB_PULLUP_PORT     (&stm_gpioa)
+#define AO_USB_PULLUP_PIN      10
+
+#define HAS_SPI_1              1       /* NHD-C12864LZ LCD Module */
+#define SPI_1_PA5_PA6_PA7      1
+#define SPI_1_PA6_DISABLE      1
+#define SPI_1_MODE_OUTPUT      STM_GPIO_CR_MODE_OUTPUT_50MHZ
+#define SPI_1_PB3_PB4_PB5      0
+#define SPI_1_PE13_PE14_PE15   0
+
+#define HAS_SPI_2              1       /* CC1200 */
+#define SPI_2_PB13_PB14_PB15   1
+#define SPI_2_PD1_PD3_PD4      0
+#define SPI_2_GPIO             (&stm_gpiod)
+#define SPI_2_SCK              1
+#define SPI_2_MISO             3
+#define SPI_2_MOSI             4
+#define SPI_2_MODE_OUTPUT      STM_GPIO_CR_MODE_OUTPUT_10MHZ
+
+#define HAS_I2C_1              0
+
+#define HAS_I2C_2              0
+
+#define PACKET_HAS_SLAVE       0
+#define PACKET_HAS_MASTER      0
+
+#define AO_FAST_TIMER          4
+#define FAST_TIMER_FREQ                10000   /* .1ms for debouncing */
+
+/* LCD module */
+#define AO_ST7565_CS_PORT      (&stm_gpioc)    /* pin 1 */
+#define AO_ST7565_CS_PIN       4
+#define AO_ST7565_RESET_PORT   (&stm_gpioc)    /* pin 2 */
+#define AO_ST7565_RESET_PIN    5
+#define AO_ST7565_A0_PORT      (&stm_gpioa)    /* pin 3 */
+#define AO_ST7565_A0_PIN       3
+#define AO_ST7565_SPI_BUS      (AO_SPI_1_PA5_PA6_PA7 | AO_SPI_MODE_3)
+#define AO_ST7565_WIDTH                128
+#define AO_ST7565_HEIGHT       64
+#define AO_ST7565_BIAS         ST7565_LCD_BIAS_1_9
+
+/*
+ * Radio is a cc1200 connected via SPI
+ */
+
+#define AO_RADIO_CAL_DEFAULT   5695733
+
+#define AO_CC1200_SPI_CS_PORT  (&stm_gpiob)
+#define AO_CC1200_SPI_CS_PIN   8
+#define AO_CC1200_SPI_BUS      AO_SPI_2_PB13_PB14_PB15
+#define AO_CC1200_SPI          stm_spi2
+
+#define AO_CC1200_INT_PORT     (&stm_gpiob)
+#define AO_CC1200_INT_PIN      (9)
+
+#define AO_CC1200_INT_GPIO     2
+#define AO_CC1200_INT_GPIO_IOCFG       CC1200_IOCFG2
+
+#define LOW_LEVEL_DEBUG                0
+
+#define HAS_LED                        1
+
+#define AO_LED_RED             AO_LED_0        /* PC7 */
+#define LED_0_PORT             (&stm_gpioc)
+#define LED_0_PIN              7
+
+#define AO_LED_AMBER           AO_LED_1        /* PC8 */
+#define LED_1_PORT             (&stm_gpioc)
+#define LED_1_PIN              8
+
+#define AO_LED_GREEN           AO_LED_2        /* PC9 */
+#define LED_2_PORT             (&stm_gpioc)
+#define LED_2_PIN              9
+
+#define AO_LED_BOX             AO_LED_3        /* PA9 */
+#define LED_3_PORT             (&stm_gpioa)
+#define LED_3_PIN              9
+
+#define AO_LED_PAD             AO_LED_4        /* PA15 */
+#define LED_4_PORT             (&stm_gpioa)
+#define LED_4_PIN              15
+
+#define AO_LED_DRAG            AO_LED_5        /* PC12 */
+#define LED_5_PORT             (&stm_gpioc)
+#define LED_5_PIN              12
+
+#define AO_LED_CONTINUITY_7    AO_LED_6        /* PC13 */
+#define LED_6_PORT             (&stm_gpioc)
+#define LED_6_PIN              13
+
+#define AO_LED_CONTINUITY_6    AO_LED_7        /* PC14 */
+#define LED_7_PORT             (&stm_gpioc)
+#define LED_7_PIN              14
+
+#define AO_LED_CONTINUITY_5    AO_LED_8        /* PC15 */
+#define LED_8_PORT             (&stm_gpioc)
+#define LED_8_PIN              15
+
+#define AO_LED_CONTINUITY_4    AO_LED_9        /* PC2 */
+#define LED_9_PORT             (&stm_gpioc)
+#define LED_9_PIN              2
+
+#define AO_LED_CONTINUITY_3    AO_LED_10       /* PC3 */
+#define LED_10_PORT            (&stm_gpioc)
+#define LED_10_PIN             3
+
+#define AO_LED_CONTINUITY_2    AO_LED_11       /* PA0 */
+#define LED_11_PORT            (&stm_gpioa)
+#define LED_11_PIN             0
+
+#define AO_LED_CONTINUITY_1    AO_LED_12       /* PA6 */
+#define LED_12_PORT            (&stm_gpioa)
+#define LED_12_PIN             6
+
+#define AO_LED_CONTINUITY_0    AO_LED_13       /* PB1 */
+#define LED_13_PORT            (&stm_gpiob)
+#define LED_13_PIN             1
+
+#define AO_LED_CONTINUITY_NUM  8
+
+#define AO_LED_REMOTE_ARM      AO_LED_14       /* PB3 */
+#define LED_14_PORT            (&stm_gpiob)
+#define LED_14_PIN             3
+
+#define AO_LED_FIRE            AO_LED_15       /* PB0 */
+#define LED_15_PORT            (&stm_gpiob)
+#define LED_15_PIN             0
+
+/*
+ * Use event queue for input devices
+ */
+
+#define AO_EVENT               1
+
+/*
+ * Knobs
+ */
+
+#define AO_QUADRATURE_COUNT    1
+#define AO_QUADRATURE_DEBOUNCE 0
+#define AO_QUADRATURE_SINGLE_CODE      1
+
+#define AO_QUADRATURE_0_PORT   &stm_gpiob
+#define AO_QUADRATURE_0_A      12
+#define AO_QUADRATURE_0_B      11
+
+#define AO_QUADRATURE_SELECT   0
+
+/*
+ * Buttons
+ */
+
+#define AO_BUTTON_COUNT                9
+#define AO_BUTTON_MODE         AO_EXTI_MODE_PULL_UP
+
+#define AO_BUTTON_DRAG_MODE    0
+#define AO_BUTTON_0_PORT       &stm_gpioc
+#define AO_BUTTON_0            1
+
+#define AO_BUTTON_DRAG_SELECT  1
+#define AO_BUTTON_1_PORT       &stm_gpioc
+#define AO_BUTTON_1            0
+
+#define AO_BUTTON_SPARE1               2
+#define AO_BUTTON_2_PORT       &stm_gpiob
+#define AO_BUTTON_2            4
+
+#define AO_BUTTON_SPARE2       3
+#define AO_BUTTON_3_PORT       &stm_gpiob
+#define AO_BUTTON_3            5
+
+#define AO_BUTTON_SPARE3               4
+#define AO_BUTTON_4_PORT       &stm_gpiob
+#define AO_BUTTON_4            6
+
+#define AO_BUTTON_ARM          5
+#define AO_BUTTON_5_PORT       &stm_gpioa
+#define AO_BUTTON_5            8
+
+#define AO_BUTTON_FIRE         6
+#define AO_BUTTON_6_PORT       &stm_gpioa
+#define AO_BUTTON_6            4
+
+#define AO_BUTTON_SPARE4       7
+#define AO_BUTTON_7_PORT       &stm_gpiob
+#define AO_BUTTON_7            7
+
+#define AO_BUTTON_ENCODER_SELECT       8
+#define AO_BUTTON_8_PORT       &stm_gpiob
+#define AO_BUTTON_8            10
+
+/* ADC */
+
+struct ao_adc {
+       int16_t         v_batt;
+};
+
+#define AO_ADC_DUMP(p) \
+       printf("batt: %5d\n", (p)->v_batt)
+
+#define HAS_ADC_SINGLE         1
+#define HAS_ADC_TEMP           0
+#define HAS_BATTERY_REPORT     1
+
+#define AO_ADC_V_BATT          2
+#define AO_ADC_V_BATT_PORT     (&stm_gpioa)
+#define AO_ADC_V_BATT_PIN      2
+
+#define AO_ADC_PIN0_PORT       AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN0_PIN                AO_ADC_V_BATT_PIN
+
+#define AO_ADC_SQ1             AO_ADC_V_BATT
+
+#define AO_NUM_ADC             1
+
+/*
+ * Voltage divider on ADC battery sampler
+ */
+#define AO_BATTERY_DIV_PLUS    15      /* 15k */
+#define AO_BATTERY_DIV_MINUS   27      /* 27k */
+
+/*
+ * ADC reference in decivolts
+ */
+#define AO_ADC_REFERENCE_DV    33
+
+#define AO_LCO_SEARCH_API
+#define AO_LCO_HAS_CONTRAST    1
+#define AO_LCO_MIN_CONTRAST    0
+#define AO_LCO_MAX_CONTRAST    63
+#define AO_LCO_CONTRAST_STEP   1
+
+#define AO_LCO_HAS_BACKLIGHT   1
+#define AO_LCO_MIN_BACKLIGHT   0
+#define AO_LCO_MAX_BACKLIGHT   65535
+#define AO_LCO_BACKLIGHT_STEP  771
+
+#define AO_LCO_HAS_INFO                1
+#define AO_LCO_MIN_INFO_PAGE   0
+#define AO_LCO_MAX_INFO_PAGE   0
+
+/*
+ * LCD Backlight via PWM.
+ *
+ * Pin PA1, TIM2_CH2
+ */
+
+#define NUM_PWM                        1
+#define PWM_MAX                        65535
+#define AO_PWM_TIMER           stm_tim2
+#define AO_LCD_BL_PWM_CHAN     1
+#define AO_PWM_0_GPIO          (&stm_gpioa)
+#define AO_PWM_0_PIN           1
+#define AO_PWM_TIMER_ENABLE    STM_RCC_APB1ENR_TIM2EN
+#define AO_PWM_TIMER_SCALE     1
+
+#define AO_AFIO_PWM_REMAP      STM_AFIO_MAPR_TIM2_REMAP
+#define AO_AFIO_PWM_REMAP_VAL  STM_AFIO_MAPR_TIM2_REMAP_PA0_PA1_PA2_PA3
+#define AO_AFIO_PWM_REMAP_MASK STM_AFIO_MAPR_TIM2_REMAP_MASK
+
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telelco-v3.0/ao_telelco.c b/src/telelco-v3.0/ao_telelco.c
new file mode 100644 (file)
index 0000000..faba96b
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_aes.h>
+#include <ao_quadrature.h>
+#include <ao_button.h>
+#include <ao_lco.h>
+#include <ao_lco_cmd.h>
+#include <ao_radio_cmac_cmd.h>
+#include <ao_eeprom.h>
+#include <ao_adc_single.h>
+#include <ao_st7565.h>
+#include <ao_pwm.h>
+
+#define WIDTH  AO_ST7565_WIDTH
+#define HEIGHT AO_ST7565_HEIGHT
+#define STRIDE AO_BITMAP_STRIDE(WIDTH)
+
+static uint32_t        image[STRIDE * HEIGHT];
+
+static struct ao_bitmap fb = {
+       .base = image,
+       .stride = STRIDE,
+       .width = WIDTH,
+       .height = HEIGHT,
+       .damage = AO_BOX_INIT,
+};
+
+static void
+ao_st7565_test(void)
+{
+       ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+       ao_st7565_update(&fb);
+       ao_text(&fb, &BitstreamVeraSans_Roman_24_font,
+               0, 20, "hello world", AO_BLACK, AO_COPY);
+       ao_st7565_update(&fb);
+}
+
+static int16_t x1 = 32, _y1 = 10, x2 = 32, y2 = 40;
+static int16_t dx1 = 2, dy1 = 2, dx2 = -2, dy2 = -1;
+
+#define bounds(v,m,M,d)        \
+               if (v < m) {                    \
+                       v = m + m - v;          \
+                       d = -d;                 \
+               } else if (v > M) {             \
+                       v = M - (v - M);        \
+                       d = -d;                 \
+               }
+
+static void
+ao_st7565_line(void)
+{
+       int     i;
+
+       for (i = 0; i < 100; i++) {
+               ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+               ao_line(&fb, x1, _y1, x2, y2, AO_BLACK, AO_COPY);
+               ao_st7565_update(&fb);
+               x1 += dx1;
+               _y1 += dy1;
+               x2 += dx2;
+               y2 += dy2;
+               printf("%d,%d - %d,%d\n", x1, _y1, x2, y2);
+               fflush(stdout);
+               bounds(x1, 0, WIDTH, dx1);
+               bounds(x2, 0, WIDTH, dx2);
+               bounds(_y1, 0, HEIGHT, dy1);
+               bounds(y2, 0, HEIGHT, dy2);
+               ao_delay(AO_MS_TO_TICKS(200));
+       }
+}
+
+static const float pad_volts = 12.3f;
+static const float lco_volts = 4.1f;
+static const int rssi = -30;
+
+static int     boxes[] = { 1, 2, 3, 5, 8, 11, 13, 17, 19, 23, 29, 31, 37, 62, 97 };
+
+//static int   max_box = 97;
+
+#define ARRAYSIZE(a)   (sizeof(a) / sizeof((a)[0]))
+
+static bool
+valid_box(int box)
+{
+       size_t i;
+       if (box == 0)
+               return true;
+       for (i = 0; i < ARRAYSIZE(boxes); i++)
+               if (boxes[i] == box)
+                       return true;
+       return false;
+}
+
+#if 0
+static void
+next_box(void)
+{
+       for (int n = box_number + 1; n <= max_box; n++)
+               if (valid_box(n)) {
+                       box_number = n;
+                       return;
+               }
+       box_number = 0;
+}
+
+static void
+prev_box(void)
+{
+       for (int n = box_number - 1; n >= 0; n--)
+               if (valid_box(n)) {
+                       box_number = n;
+                       return;
+               }
+       box_number = max_box;
+}
+#endif
+
+static const struct ao_transform logo_transform = {
+       .x_scale = 48, .x_off = 2,
+       .y_scale = 48, .y_off = 0,
+};
+
+#define BIG_FONT BitstreamVeraSans_Roman_58_font
+#define VOLT_FONT BitstreamVeraSans_Roman_58_font
+#define SMALL_FONT BitstreamVeraSans_Roman_12_font
+#define TINY_FONT BitstreamVeraSans_Roman_10_font
+#define LOGO_FONT BenguiatGothicStd_Bold_26_font
+
+#define LABEL_Y                (int16_t) (SMALL_FONT.ascent)
+#define VALUE_Y                (int16_t) (LABEL_Y + BIG_FONT.ascent + 5)
+#define BOX_X          2
+#define PAD_X          90
+#define BOX_LABEL_X    30
+#define VOLT_LABEL_X   25
+#define RSSI_LABEL_X   15
+#define PAD_LABEL_X    95
+#define SEP_X          (PAD_X - 8)
+#define SCAN_X         (WIDTH - 100) / 2
+#define SCAN_Y         50
+#define SCAN_HEIGHT    3
+#define FOUND_Y                63
+#define FOUND_X                6
+#define FOUND_WIDTH    17
+#define MAX_VALID      (WIDTH / FOUND_WIDTH)
+
+static int16_t box_number = 88;
+static int16_t pad_number = 8;
+
+static void
+ao_st7565_poly(void)
+{
+       int16_t scan_number;
+       char    str[8];
+       int     i;
+       int     v;
+       int     last_box;
+       int16_t b;
+
+       for (scan_number = 0; scan_number < 100; scan_number++) {
+               ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+               ao_logo(&fb, &logo_transform, &LOGO_FONT, AO_BLACK, AO_COPY);
+               if (scan_number) {
+                       ao_rect(&fb, SCAN_X, SCAN_Y, (int16_t) scan_number, SCAN_HEIGHT, AO_BLACK, AO_COPY);
+                       b = 0;
+                       v = 0;
+                       last_box = 0;
+                       for (i = scan_number; i > 1; i--) {
+                               if (valid_box(i)) {
+                                       if (!last_box)
+                                               last_box = i;
+                                       v++;
+                                       if (v == MAX_VALID)
+                                               break;
+                               }
+                       }
+                       for (; i <= scan_number; i++) {
+                               if (valid_box(i)) {
+                                       sprintf(str, "%02d%s", i, i == last_box ? "" : ",");
+                                       ao_text(&fb, &TINY_FONT, (int16_t) (FOUND_X + FOUND_WIDTH * b),
+                                               FOUND_Y, str, AO_BLACK, AO_COPY);
+                                       b++;
+                               }
+                       }
+               }
+               ao_st7565_update(&fb);
+               ao_delay(AO_MS_TO_TICKS(50));
+       }
+       ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
+       switch (box_number) {
+       case 0:
+               sprintf(str, "%4.1f", lco_volts);
+               ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+               ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, "LCO Battery", AO_BLACK, AO_COPY);
+               break;
+       default:
+               switch (pad_number) {
+               case -1:
+                       sprintf(str, "%4.1f", pad_volts);
+                       ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+                       ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, "Pad Battery", AO_BLACK, AO_COPY);
+                       break;
+               case 0:
+                       sprintf(str, "%4d", rssi);
+                       ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+                       ao_text(&fb, &SMALL_FONT, RSSI_LABEL_X, LABEL_Y, "Signal Strength", AO_BLACK, AO_COPY);
+                       break;
+               default:
+                       sprintf(str, "%02d", box_number);
+                       ao_text(&fb, &BIG_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+                       ao_text(&fb, &SMALL_FONT, BOX_LABEL_X, LABEL_Y, "Box", AO_BLACK, AO_COPY);
+
+                       sprintf(str, "%d", pad_number);
+                       ao_text(&fb, &BIG_FONT, PAD_X, VALUE_Y, str, AO_BLACK, AO_COPY);
+                       ao_text(&fb, &SMALL_FONT, PAD_LABEL_X, LABEL_Y, "Pad", AO_BLACK, AO_COPY);
+
+                       ao_rect(&fb, SEP_X, 0, 2, HEIGHT, AO_BLACK, AO_COPY);
+               }
+               break;
+       }
+       ao_st7565_update(&fb);
+}
+
+const struct ao_cmds ao_st7565_cmds[] = {
+       { ao_st7565_test, "g\0Test ST7565 display" },
+       { ao_st7565_line, "l\0Draw lines" },
+       { ao_st7565_poly, "p\0Draw polygon" },
+       { 0, NULL },
+};
+
+int
+main(void)
+{
+       ao_clock_init();
+
+       ao_led_init();
+       ao_task_init();
+
+       ao_timer_init();
+
+       ao_spi_init();
+       ao_dma_init();
+       ao_exti_init();
+       ao_adc_single_init();
+
+       ao_beep_init();
+       ao_pwm_init();
+       ao_cmd_init();
+
+       ao_quadrature_init();
+       ao_button_init();
+
+       ao_radio_init();
+
+       ao_usb_init();
+
+       ao_st7565_init();
+
+       ao_config_init();
+
+       ao_lco_init();
+       ao_lco_cmd_init();
+
+//     ao_cmd_register(ao_st7565_cmds);
+
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/telelco-v3.0/flash-loader/Makefile b/src/telelco-v3.0/flash-loader/Makefile
new file mode 100644 (file)
index 0000000..8f3717b
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telelco-v3.0
+include $(TOPDIR)/stm32f1/Makefile-flash.defs
diff --git a/src/telelco-v3.0/flash-loader/ao_pins.h b/src/telelco-v3.0/flash-loader/ao_pins.h
new file mode 100644 (file)
index 0000000..8516d48
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2018 Bdale Garbee <bdale@gag.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* 16MHz crystal */
+
+#define AO_HSE         1
+#define AO_HSE_BYPASS  0
+
+#define AO_SYSCLK      72000000
+#define AO_HCLK                72000000
+#define AO_APB1CLK     36000000
+#define AO_APB2CLK     72000000
+#define AO_ADCCLK      12000000
+
+/* PLLMUL is 9, PLLXTPRE (pre divider) is 2, so the
+ * overall PLLCLK is 16 * 9/2 = 72MHz (used as SYSCLK)
+ *
+ * HCLK is SYSCLK / 1 (HPRE_DIV) = 72MHz (72MHz max)
+ * USB is PLLCLK / 1.5 (USBPRE)= 48MHz (must be 48MHz)
+ * APB2 is HCLK / 1 (PPRE2_DIV) = 72MHz (72MHz max)
+ * APB1 is HCLK / 2 (PPRE1_DIV) = 36MHz (36MHz max)
+ * ADC is APB2 / 6 (ADCPRE) = 12MHz (14MHz max)
+ */
+
+#define AO_RCC_CFGR_USBPRE     STM_RCC_CFGR_USBPRE_1_5
+#define AO_RCC_CFGR_PLLMUL     STM_RCC_CFGR_PLLMUL_9
+#define AO_RCC_CFGR_PLLXTPRE   STM_RCC_CFGR_PLLXTPRE_2
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_1
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE1_DIV_2
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+#define AO_RCC_CFGR_ADCPRE     STM_RCC_CFGR_ADCPRE_6
+
+#include <ao_flash_stm_pins.h>
+
+/* Companion port cs_companion0 PC10 */
+
+#define AO_BOOT_PIN            1
+#define AO_BOOT_APPLICATION_GPIO       stm_gpioc
+#define AO_BOOT_APPLICATION_PIN                10
+#define AO_BOOT_APPLICATION_VALUE      1
+#define AO_BOOT_APPLICATION_MODE       AO_EXTI_MODE_PULL_UP
+
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT     (&stm_gpioa)
+#define AO_USB_PULLUP_PIN      10
+
+#endif /* _AO_PINS_H_ */
index cbced6ae31435fb870ee8c599c02d88f9d564d5a..7c4e6f0e627283ed08f8d7214d20592ad8f42704 100644 (file)
@@ -303,8 +303,31 @@ ao_real_packet(void)
        return ok;
 }
 
+int
+ao_hello_packet(void)
+{
+       uint8_t message[5] = "hello";
+       uint8_t encode[ENCODE_LEN(sizeof(message))];
+       int encode_len;
+       uint8_t transmit[EXPAND_LEN(sizeof(message))];
+       uint8_t decode[DECODE_LEN(sizeof(message))];
+       int transmit_len;
+       int decode_ok;
+
+       printf("Hello packet test:\n");
+       ao_fec_dump_bytes(message, sizeof(message), "Message");
+       encode_len = ao_fec_encode(message, sizeof(message), encode);
+       ao_fec_dump_bytes(encode, encode_len, "Encode");
+       transmit_len = ao_expand(encode, encode_len, transmit);
+       ao_fec_dump_bytes(transmit, transmit_len, "Transmit");
+       decode_ok = ao_fec_decode(transmit, transmit_len, decode, sizeof(message) + 2, NULL);
+       ao_fec_dump_bytes(decode, sizeof(message) + 2, "Receive");
+       printf("Hello result: %s\n", decode_ok ? "success" : "fail");
+       return decode_ok;
+}
+
 #define EXPECT_DECODE_FAIL     0
-#define EXPECT_CRC_MISMATCH    6386
+#define EXPECT_CRC_MISMATCH    6304
 #define EXPECT_DATA_MISMATCH   0
 #define NOISE_AMOUNT           0x50
 
@@ -336,6 +359,9 @@ main(int argc, char **argv)
        if (!ao_real_packet())
                errors++;
 
+       if (!ao_hello_packet())
+               errors++;
+
        srandom(0);
        for (trial = 0; trial < 100000; trial++) {
 
index b5d267b75d2e0c73916e13d9b6bb9dfe22a838c4..a4a60b6f267670f07591d1d5f41d5ed56d748d77 100644 (file)
@@ -41,6 +41,7 @@ public class TeleGPSConfigUI
        JLabel                  radio_enable_label;
        JLabel                  radio_10mw_label;
        JLabel                  report_feet_label;
+       JLabel                  gps_receiver_label;
        JLabel                  rate_label;
        JLabel                  aprs_interval_label;
        JLabel                  aprs_ssid_label;
@@ -62,6 +63,7 @@ public class TeleGPSConfigUI
        JRadioButton            radio_enable_value;
        JRadioButton            radio_10mw_value;
        JComboBox<String>       report_feet_value;
+       JComboBox<String>       gps_receiver_value;
        AltosUIRateList         rate_value;
        JComboBox<String>       aprs_interval_value;
        JComboBox<Integer>      aprs_ssid_value;
@@ -200,6 +202,39 @@ public class TeleGPSConfigUI
                        return AltosLib.MISSING;
        }
 
+       void set_gps_receiver_tool_tip() {
+               if (gps_receiver_value.isVisible())
+                       gps_receiver_value.setToolTipText("GPS receiver selection");
+               else
+                       gps_receiver_value.setToolTipText("Only TeleMega with new firmware supports alternate GPS receivers");
+       }
+
+       public void set_gps_receiver(int new_gps_receiver) {
+               System.out.printf("set_gps_receiver %d\n", new_gps_receiver);
+               if (new_gps_receiver != AltosLib.MISSING) {
+                       if (new_gps_receiver >= AltosLib.gps_receiver_names.length)
+                               new_gps_receiver = 0;
+                       if (new_gps_receiver < 0) {
+                               gps_receiver_value.setEnabled(false);
+                               new_gps_receiver = 0;
+                       } else {
+                               gps_receiver_value.setEnabled(true);
+                       }
+                       gps_receiver_value.setSelectedIndex(new_gps_receiver);
+               }
+               gps_receiver_value.setVisible(new_gps_receiver != AltosLib.MISSING);
+               gps_receiver_label.setVisible(new_gps_receiver != AltosLib.MISSING);
+
+               set_gps_receiver_tool_tip();
+       }
+
+       public int gps_receiver() {
+               if (gps_receiver_value.isVisible())
+                       return gps_receiver_value.getSelectedIndex();
+               else
+                       return AltosLib.MISSING;
+       }
+
        void set_rate_tool_tip() {
                if (rate_value.isVisible())
                        rate_value.setToolTipText("Select telemetry baud rate");
@@ -248,6 +283,8 @@ public class TeleGPSConfigUI
                        flight_log_max_value.setToolTipText("Cannot set max value with flight logs in memory");
        }
 
+       public boolean has_radio() { return true; }
+
        /* Build the UI using a grid bag */
        public TeleGPSConfigUI(JFrame in_owner) {
                super (in_owner, "Configure Device", false);
@@ -428,6 +465,32 @@ public class TeleGPSConfigUI
                set_report_feet_tool_tip();
                row++;
 
+               /* GPS Receiver */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               gps_receiver_label = new JLabel("GPS Receiver:");
+               pane.add(gps_receiver_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               gps_receiver_value = new JComboBox<String>(AltosLib.gps_receiver_names);
+               gps_receiver_value.setEditable(false);
+               gps_receiver_value.addItemListener(this);
+               pane.add(gps_receiver_value, c);
+               set_gps_receiver_tool_tip();
+               row++;
+
                /* Radio 10mW limit */
                c = new GridBagConstraints();
                c.gridx = 0; c.gridy = row;