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