Compare commits

...

136 Commits

Author SHA1 Message Date
King Kévin 2e917bed9e README: minor, fix typo 2021-05-10 12:41:45 +02:00
King Kévin 0fc51b9422 README: minor, fix typo 2021-05-10 12:41:06 +02:00
King Kévin 03a7872bb9 README: minor, fix typo 2021-05-10 12:40:02 +02:00
King Kévin d3fbf75b36 application: minor, rename autodetect to tx 2021-05-10 12:34:30 +02:00
King Kévin c98e82fedf application: add command to probe for UART RX pin 2021-05-10 12:33:55 +02:00
King Kévin 3ba5b95061 README: document UART autodetection command 2021-05-09 14:46:26 +02:00
King Kévin 666ae736ef application: add UART autodetection (copied from BusVoodoo) 2021-05-08 10:34:15 +02:00
King Kévin 1aa498d5bc usart_enhanced: mark STM32F4 compatible (no difference with STM32F1) 2021-05-08 10:25:21 +02:00
King Kévin c9dfb09ace usart_enhanced: minor, fix spacing 2021-05-08 10:20:22 +02:00
King Kévin 16cb734f6f application: add frequency measurement in monitor single mode 2021-05-07 12:16:24 +02:00
King Kévin 97818981fb README: document single channel monitoring 2021-05-06 11:07:05 +02:00
King Kévin 3370e35dc5 application: add single channel monitoring 2021-05-06 10:51:05 +02:00
King Kévin 611f5f6683 application: fix channels pinout 2021-05-05 14:55:17 +02:00
King Kévin 6bb7e001aa application: monitor, make increment overflow safe 2021-05-05 14:54:13 +02:00
King Kévin 9c340c6a41 application: monitor, show vhigh 2021-05-05 14:53:43 +02:00
King Kévin ebaf30a25f application: minor, improve doc 2021-05-05 14:53:12 +02:00
King Kévin f4b02de076 README: fix typo 2021-05-03 13:38:58 +02:00
King Kévin d4e081a280 application: use X instead of x for activity 2021-05-03 13:38:42 +02:00
King Kévin 11c8cee3ee README: document project and schematic 2021-05-03 12:40:52 +02:00
King Kévin f603b1398e application: show multiple changes when monitoring (and rate limit output) 2021-05-03 12:39:46 +02:00
King Kévin 436c925038 application: fix set target voltage doc 2021-05-03 12:37:10 +02:00
King Kévin 3959eabb84 applciation: fix set target voltage 2021-05-03 12:36:26 +02:00
King Kévin 734be00a82 application: add timer for monitor command activity 2021-03-25 14:48:40 +01:00
King Kévin 577e5511a2 application: add basic monitor command 2021-03-25 12:37:53 +01:00
King Kévin a4d881df29 application: fix type floating detection 2021-03-25 12:37:39 +01:00
King Kévin 86fd05f52c application: improve type result output 2021-03-24 17:33:54 +01:00
King Kévin 2cccdd4d6e application: make some text only output when DEBUG is set 2021-03-24 17:33:33 +01:00
King Kévin 165f545310 global: disable DEBUG 2021-03-24 17:06:12 +01:00
King Kévin 0047fb8fea application: remove watchdog info (not F4 compatible) 2021-03-24 17:05:21 +01:00
King Kévin 64706680c2 application: minor, fix typo 2021-03-24 17:04:04 +01:00
King Kévin eb4946e7ad application: remove date command 2021-03-24 00:56:03 +01:00
King Kévin 423a0ec90b application: add command to identify I/O channels 2021-03-24 00:54:53 +01:00
King Kévin 89856091c7 application: add command to set channels 2021-03-24 00:48:51 +01:00
King Kévin ce9d927a5b application: add command to control target reset 2021-03-24 00:47:59 +01:00
King Kévin caed09f4db application: add command to set/show target voltage 2021-03-24 00:46:30 +01:00
King Kévin bb15fdc634 application: add channel initialisation 2021-03-24 00:44:47 +01:00
King Kévin 0ad8e037cf application: add control for supply voltages 2021-03-24 00:43:46 +01:00
King Kévin df2dfe0cb4 application: add multiplex control 2021-03-24 00:42:25 +01:00
King Kévin d55dbc34b2 application: add ADC to measure target voltage 2021-03-24 00:39:56 +01:00
King Kévin da626f7780 application: use LSI clock for RTC 2021-03-24 00:23:47 +01:00
King Kévin c67c9a73ef application: remove LED since we will use the pin 2021-03-24 00:21:58 +01:00
King Kévin 4dc895d793 application: remove button since we will use the pins 2021-03-24 00:20:11 +01:00
King Kévin 48c1b5800e application: remove UART since we will use the pins 2021-03-24 00:18:45 +01:00
King Kévin 370c7960fb application: set project name 2021-03-24 00:10:18 +01:00
King Kévin b3cf0d0302 swd: improve documentation 2021-03-23 17:22:48 +01:00
King Kévin 3a6a64928d swd: minor, fix doc 2021-03-23 17:22:48 +01:00
King Kévin 99d66f4e4e swd: improve part number decoding 2021-03-23 17:22:48 +01:00
King Kévin 66521e1981 swd: minor, fix comment, add doc, make reset tiny bit longer for better reliability 2021-03-23 17:22:48 +01:00
King Kévin 210fab8eae swd: expose release pins 2021-03-23 17:22:48 +01:00
King Kévin 7318d70dcd swd: minor, fix space 2021-03-23 17:22:48 +01:00
King Kévin 0010c5e046 swd: provide function to set SWCLK/SWDIO pin 2021-03-23 17:22:48 +01:00
King Kévin 6b3b55839e swd: use variables for pins (for later dynamic change) 2021-03-23 17:22:48 +01:00
King Kévin 6bed3ab0fb Rakefile: ingnore .inc files 2021-03-23 17:22:48 +01:00
King Kévin 9a7c51f80e Rakefile: fix spacing 2021-03-23 17:22:48 +01:00
King Kévin 7a74f9709f add SWD library 2021-03-23 17:22:48 +01:00
King Kévin fa29cfc29f application: minor, fix typo 2020-12-17 12:51:23 +01:00
King Kévin 2248ba1762 application: fix rtc_to_seconds 2020-12-17 12:51:23 +01:00
King Kévin ac255816a1 sensor_mlx90614: add library to read from MLX90614 IR-thermometer 2020-12-17 12:51:23 +01:00
King Kévin 95b63a06f5 smbus_master: add SMBus library 2020-12-17 12:51:23 +01:00
King Kévin 7656c699bf i2c_master: fix stop generation 2020-12-17 12:51:23 +01:00
King Kévin c6a4f58b93 i2c_master: fix wait_stop call 2020-12-17 12:51:23 +01:00
King Kévin b82520fa9b sensor_sr04: fix shadow counter value issue 2020-12-17 12:51:23 +01:00
King Kévin 25fcf8fe0b global: add ADC macros 2020-12-17 12:51:23 +01:00
King Kévin 01eaa5cfab USB: increase text buffer size for project 2020-12-17 12:51:23 +01:00
King Kévin 793611d629 application: implement uptime 2020-12-17 12:48:37 +01:00
King Kévin ad52abc26b oled_ssd1306: adapt to ported I²C library 2020-12-17 12:48:37 +01:00
King Kévin b0f5f127f6 i2c_master: port to STM32F4 2020-12-17 12:48:37 +01:00
King Kévin a449b9b7ff global: add I²C macros 2020-12-17 12:48:37 +01:00
King Kévin 4c6e9a4fda interrupt: port to STM32F4 2020-12-17 12:48:37 +01:00
King Kévin 789b36fc21 interrupt: minor, fix comment 2020-12-17 12:48:37 +01:00
King Kévin c8861f40c4 onewire_master: port to STM32F4 2020-12-17 12:48:37 +01:00
King Kévin 77415cb41f onewire_master: minor, fix spacing 2020-12-17 12:48:37 +01:00
King Kévin 11f5bc9771 sensor_sr04: add library for HC-SR04 ultrasonic range sensor 2020-12-17 12:48:37 +01:00
King Kévin 8526dc084b global: add tim irq defines 2020-12-17 12:48:37 +01:00
King Kévin fea286914b global: improve sleep_us for STM32F4 2020-12-17 12:48:37 +01:00
King Kévin cfcc8a1bb6 Rakefile: automatically get libopencm3 2020-12-17 12:48:37 +01:00
King Kévin 510c82d00f Merge branch 'stm32f4' of ssh://git.cuvoodoo.info/stm32f1 into stm32f4 2020-12-11 00:03:15 +01:00
King Kévin 26f6de3015 sensor_max1247: STM32F4 incompatible for now 2020-12-11 00:02:44 +01:00
King Kévin a9461b53f5 README: port to F4 2020-12-11 00:00:25 +01:00
King Kévin d7b6300a50 rakefile: fix remove protection for F4 2020-12-11 00:00:25 +01:00
King Kévin d0bd71b266 application: add periodis RTC wakeup 2020-12-11 00:00:25 +01:00
King Kévin a46b6a1630 Rakefile: add macro debugging information 2020-12-11 00:00:25 +01:00
King Kévin b100c4ae13 application: RTC + date/time added 2020-12-11 00:00:25 +01:00
King Kévin a0f9b4a530 application: port to STM32F4 (RTC is not working yet) 2020-12-11 00:00:25 +01:00
King Kévin e32e27100d USB CDC ACM: fix sending loop (and spacing) 2020-12-11 00:00:25 +01:00
King Kévin 5b0523f751 uart: port to STM32F4 2020-12-11 00:00:25 +01:00
King Kévin d6cac41b78 USB CDC ACM: minor fix spacing 2020-12-11 00:00:25 +01:00
King Kévin adc62ebb9a USB CDC ACM: port to STM32F4 2020-12-11 00:00:25 +01:00
King Kévin d9a15f2daa USB CDC ACM: match serial to STM32 bootloader 2020-12-11 00:00:25 +01:00
King Kévin c4af940975 dfu: minor, improve disconnect 2020-12-11 00:00:25 +01:00
King Kévin c58d27cf2e Rakefile: add method to flash bootloader over DFU 2020-12-11 00:00:25 +01:00
King Kévin c3d7711258 global: add synchronisation barrier commands 2020-12-11 00:00:25 +01:00
King Kévin 78cb85421a global: add common function to start DFU and systeme memory 2020-12-11 00:00:25 +01:00
King Kévin ff5fbc847d DFU: fix DP pull down 2020-12-11 00:00:25 +01:00
King Kévin 51e0bfd188 DFU: minor, remove unused/duplicate code 2020-12-11 00:00:25 +01:00
King Kévin c411d552a1 DFU: set serial to match STM32 DFU bootloader 2020-12-11 00:00:25 +01:00
King Kévin ceff33ea0e Rakefile: use derivated device properties 2020-12-11 00:00:25 +01:00
King Kévin 68955ddfec bootloader: update to work with F4 2020-12-11 00:00:25 +01:00
King Kévin 40ee01ce67 usb_dfu: update to work with F4 2020-12-11 00:00:25 +01:00
King Kévin 0b2bbf8c97 libopencm3: use branch with OTG fix
because the MINIF4 board does not have an optional pull-up resistor on D+, the device is not enumerated without this fix.
this fix is not yet in official libopencm3 master.
2020-12-11 00:00:25 +01:00
King Kévin 87af738378 flash_internal: remove F1 flash utilities, add F4 section utility
compared to the STM32F1, the STM32F4 does not used 1 KB flash pages.
F4 uses variable large (>= 16 KB) flash sections.
this makes using the last page (128 KB instead of 1KB) for EEPROM highly inefficient.
caching such large pages before reprogramming small portion is also no doable (there is not enough RAM).
thus almost all F1 utilities are not applicable anymore.
to help erasing the right section, a utility to get the section from an address is added.
2020-12-11 00:00:25 +01:00
King Kévin e4ce622f15 terminal: minor, fix doc 2020-12-11 00:00:25 +01:00
King Kévin dbd0ea4d27 global: remove macro pin definition since on F4 they are not unique 2020-12-11 00:00:25 +01:00
King Kévin a878a1ad9c global: define MINIF401 button/led pins 2020-12-11 00:00:25 +01:00
King Kévin aff4275478 lib: disable most libraries since they need tuning to be F4 compatible 2020-12-11 00:00:25 +01:00
King Kévin 609188d74e Rakefile: compile for STM32F4 2020-12-11 00:00:25 +01:00
King Kévin 63a2e5e5ff *.ld: set flash and RAM size for STM32F401xC 2020-12-11 00:00:25 +01:00
King Kévin ac1bea1d45 README: port to F4 2020-11-30 15:03:32 +01:00
King Kévin 7b7f26ee47 rakefile: fix remove protection for F4 2020-11-30 14:51:06 +01:00
King Kévin 3d00bdf3c0 application: add periodis RTC wakeup 2020-11-30 14:36:33 +01:00
King Kévin 319a02d2b4 Rakefile: add macro debugging information 2020-11-28 15:19:13 +01:00
King Kévin cc8be1f278 application: RTC + date/time added 2020-11-28 15:17:52 +01:00
King Kévin e255573b1e application: port to STM32F4 (RTC is not working yet) 2020-11-27 17:07:39 +01:00
King Kévin 0fe7e1fd39 USB CDC ACM: fix sending loop (and spacing) 2020-11-27 17:06:21 +01:00
King Kévin 2249f460e3 uart: port to STM32F4 2020-11-27 16:49:59 +01:00
King Kévin aae4009fbe USB CDC ACM: minor fix spacing 2020-11-27 16:44:17 +01:00
King Kévin a9284b7154 USB CDC ACM: port to STM32F4 2020-11-27 16:43:57 +01:00
King Kévin 777fd7afb9 USB CDC ACM: match serial to STM32 bootloader 2020-11-27 16:41:19 +01:00
King Kévin 31079d95dd dfu: minor, improve disconnect 2020-11-27 16:39:51 +01:00
King Kévin ced714129c Rakefile: add method to flash bootloader over DFU 2020-11-27 16:39:11 +01:00
King Kévin 4fcfd29d2b global: add synchronisation barrier commands 2020-11-27 16:38:32 +01:00
King Kévin 06de8d0be9 global: add common function to start DFU and systeme memory 2020-11-27 16:37:52 +01:00
King Kévin de36c7f3a2 DFU: fix DP pull down 2020-11-27 16:05:55 +01:00
King Kévin 8918b97618 DFU: minor, remove unused/duplicate code 2020-11-27 16:05:37 +01:00
King Kévin 0bb2be3727 DFU: set serial to match STM32 DFU bootloader 2020-11-27 16:04:07 +01:00
King Kévin a781fc5b3b Rakefile: use derivated device properties 2020-11-27 15:54:08 +01:00
King Kévin 00ef5d9344 bootloader: update to work with F4 2020-11-24 16:18:17 +01:00
King Kévin 9fbf5b4aad usb_dfu: update to work with F4 2020-11-24 16:17:37 +01:00
King Kévin 46083bdf5e libopencm3: use branch with OTG fix
because the MINIF4 board does not have an optional pull-up resistor on D+, the device is not enumerated without this fix.
this fix is not yet in official libopencm3 master.
2020-11-24 16:11:01 +01:00
King Kévin 8a165c4d71 flash_internal: remove F1 flash utilities, add F4 section utility
compared to the STM32F1, the STM32F4 does not used 1 KB flash pages.
F4 uses variable large (>= 16 KB) flash sections.
this makes using the last page (128 KB instead of 1KB) for EEPROM highly inefficient.
caching such large pages before reprogramming small portion is also no doable (there is not enough RAM).
thus almost all F1 utilities are not applicable anymore.
to help erasing the right section, a utility to get the section from an address is added.
2020-11-24 16:04:42 +01:00
King Kévin 9db9ea9dc1 terminal: minor, fix doc 2020-11-24 16:01:49 +01:00
King Kévin 4b514c6801 global: remove macro pin definition since on F4 they are not unique 2020-11-24 16:01:06 +01:00
King Kévin 6a34352914 global: define MINIF401 button/led pins 2020-11-24 15:59:42 +01:00
King Kévin 35c441355d lib: disable most libraries since they need tuning to be F4 compatible 2020-11-24 15:56:00 +01:00
King Kévin 9751880813 Rakefile: compile for STM32F4 2020-11-24 15:51:03 +01:00
King Kévin e58614002c *.ld: set flash and RAM size for STM32F401xC 2020-11-24 15:48:25 +01:00
58 changed files with 5403 additions and 1564 deletions

3
.gitmodules vendored
View File

@ -1,4 +1,5 @@
[submodule "libopencm3"]
path = libopencm3
url = https://github.com/libopencm3/libopencm3
url = https://github.com/manuelbl/libopencm3
ignore = all
branch = no-vbus-sensing

213
README.md
View File

@ -1,4 +1,4 @@
This firmware template is designed for development boards based around [STM32 F1 series micro-controller](http://www.st.com/web/en/catalog/mmc/FM141/SC1169/SS1031).
firmware is for the I/O finder.
project
=======
@ -6,50 +6,172 @@ project
summary
-------
*describe project purpose*
this tools allow to identify if channels (e.g. lines) are inputs or outputs.
up to 16 channels can be identified at once.
it also allows to monitor the channel activity, identify UART TX and RX signals.
usage
-----
connect the pins or test points of the target device to the I/O finder channels pins on the connector.
select the first and last channel to probe using the `start <CH>` ans `stop <CH>` commands.
this prevents false activity monitoring alerts.
it will also make the scan faster.
also connect the target voltage pin to the SWJ finder in order to use the right signal voltage level.
alternatively, the SWJ finder can supply 3.3V to the target voltage pin using the `voltage 3` command.
to revert to using the externally provided target voltage, use the `voltage 0` command.
to measure the target voltage, use the `voltage` command.
to find out what type of I/O is connected to the channel, use the `type` command.
it will measure the voltage while applying a pull-up and pull-down.
if the voltage changes, the channel is likely an input (or floating).
it will also show what value of pull-up/down is already on the channel.
if the voltage does not change, the channel is likely an output (or GND/power supply rail).
be aware that this is just the result at the testing type, as the target can change its configuration.
to monitor the activity of the channels, use the `monitor` command.
provide no argument to not pull the channels.
provide the 0 argument to pull the channels low using the internal 40 kOhm resistor.
provide the 3 argument to pull the channels high to 3.3V using the internal 40 kOhm resistor.
pulling the channels may reduce the noise, in case the channel is floating.
the activity and current state (with time stamp) of the pin will be displayed (up to 0.1s speed).
if the channel is switch fast, an 'X' will appear in from of the current level.
to stop monitoring, press any key.
to monitor the activity of a single channel, use the `monitor_single` command.
provide as argument the channel you want to monitor.
this works like the `monitor` command, but has the advantage to detect high voltages down to 1.5V (ideal for 1.8V logic).
it will also show the (maximum) frequency of the signal (useful to clock and baud rate measurement).
to detect the configuration of a UART stream, use the `uart_tx` command.
provide as argument the channel you want to monitor.
as data comes it, it will figure out the baud rate, data bits, and parity of the stream.
the configuration is displayed as it finds a better match.
it will also show the decoded data.
this mainly works for ASCII based communication (e.g. human readable debug logs), and might not give correct results for binary communication.
to find the corresponding UART RX pin (e.g. once UART TX pin has been identified using the `uart_tx` command), use the `uart_rx` commands.
provide as argument the channel for the UART TX pin, and the UART baud rate and frame settings (e.g. 115200 8N1).
data will be transmitted on the channels to probe and if we received the echo back on the TX, we found the RX.
this detection method only works if the target echoes back the data (e.g. on login prompts).
you can also reset the target board if you connected to target reset pin to the SWJ finder.
you can select of to drive the reset pin (OD for open-drain, PP for push-pull) and active level (H for high, L for low) using the `reset [ODL|ODH|PPL|PPH]` command.
to assert or release the reset, us the `reset 1` or `reset 0` commands.
alternatively, pressing/releasing the button on the SWJ finder asserts/releases the reset signal
use the `help` command to list all commands.
this will also list the shortcuts for the commands.
technology
----------
*described electronic details*
to figure out the type of I/O a channel is, it:
- selects the channel on a analog multiplexer
- measures the voltage while applying a 2 kOhm pull-down (to ground)
- measures the voltage while applying a 2 kOhm pull-up (to target voltage)
- calculate the resistance on the channel (since it known the resistor values of the voltage divided used to measure the channel voltage)
to monitor the activity, it directly reads the level of all 16 channels in parallel.
no voltage shifter is used (the input have a high impedance to not affect the channels).
any voltage above 3.3*0.7=2.3V is detected as high, else it is low.
limitation
----------
the target voltage should not be higher than 5.5V (board I/O-pins limitation).
the channel voltage level need to be above 2.3V for activity to be detected reliably when multiple channels are monitored.
the channel voltage level need to be above 1.5V for activity to be detected reliably when a single channel is monitored.
the reset pin has no inline protection resistor and can sink up to 25 mA.
in open drain mode, an external pull-up resistor is required, most often provide by the target device.
in push-pull mode it can only source 3.3V up to 25 mA.
board
=====
The current implementation uses a [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board).
The underlying template also supports following board:
- [Maple Mini](http://leaflabs.com/docs/hardware/maple-mini.html), based on a STM32F103CBT6
- [System Board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#system_board), based on a STM32F103C8T6
- [blue pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill), based on a STM32F103C8T6
- [black pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#black_pill), based on a STM32F103C8T6
- [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board), based on a STM32F103C8T6
- [ST-LINK V2 mini](https://wiki.cuvoodoo.info/doku.php?id=jtag#mini_st-link_v2), a ST-LINK/V2 clone based on a STM32F101C8T6
- [USB-Blaster](https://wiki.cuvoodoo.info/doku.php?id=jtag#armjishu_usb-blaster), an Altera USB-Blaster clone based on a STM32F101C8T6
**Which board is used is defined in the Makefile**.
This is required to map the user LED and button provided on the board
The ST-LINK V2 mini clone has SWD test points on the board.
Because read protection is enabled, you will first need to remove the protection to be able to flash the firmware.
To remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected.
The Altera USB-Blaster clone has a pin header for SWD and UART1 on the board.
SWD is disabled in the main firmware, and it has read protection.
To be able to flash using SWD (or the serial port), the BOOT0 pin must be set to 1 to boot the system memory install of the flash memory.
To set BOOT0 to 1, apply 3.3 V on R11, between the resistor and the reference designator, when powering the device.
The red LED should stay off while the green LED is on.
Now you can remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected.
the underlying hardware uses a [WeAct MiniF4](https://github.com/WeActTC/MiniF4-STM32F4x1) board, based on a STM32F401CCU6.
the bi-directional level shifter are BSS138 n-channel MOSFET based.
switching the target voltage are done using BSS84 p-channel MOSFET.
connections
===========
Connect the peripherals the following way (STM32F10X signal; STM32F10X pin; peripheral pin; peripheral signal; comment):
remove LED on C13 on MiniF4 board so we can reuse the pin.
- *list board to peripheral pin connections*
IDC 2x10 connector:
All pins are configured using `define`s in the corresponding source code.
01. GND, connect to ground
02. VTRG, target voltage, see below for connection
03. RST, to reset the target board, connect to PA0
04. 3V3, connected to 3.3V
05. CH00, connect to PB10
06. CH01, connect to PB9
07. CH02, connect to PB8
08. CH03, connect to PB7
09. CH04, connect to PB6
10. CH05, connect to PB5
11. CH06, connect to PB4
12. CH07, connect to PB3
13. CH08, connect to PA15
14. CH09, connect to PA10
15. CH10, connect to PA9
16. CH11, connect to PA8
17. CH12, connect to PB15
18. CH13, connect to PB14
19. CH14, connect to PB13
20. CH15, connect to PB12
ADC, used to measure target voltage (up to 6.6V):
- connect VTRG to 22 kOhm resistor R1
- connect PA6 (ADC1_IN6) to other side of resistor R1
- connect PA6 to 22 kOhm resistor R2
- connect GND to other side of resistor R2, forming a voltage divided
analog multiplexer, 16-channel, HP4067, to probe individual channels:
- channels: C0-C15 to CH0-CH15
- select: S3 to PB2, S2 to PB1, S1 to PB0, S0 to PA7
- enable: EN to PC15, pulled up to VCC
- signal: S to see below
- VCC: to 5V (for better switching, the select line can still be operated at 3.3V)
- GND: to ground
ADC for signal, using BSS84 p-channel MOSFET Q5:
- connect signal S to PA1/ADC1_IN1 through 1 KOhm resistor (first part of voltage divider)
- connect 1 kOhm between PA1 and PA4, creating second part of voltage divider (PA4 can ground it)
- Q5 source: to VTRG
- Q5 gate: to PA5, pulled up to VTRG using 10-100 kOhm resistor
- Q5 drain: to PA4 (PA5 can set connect voltage divider to high side)
UART, with level shifter, to scan for UART port:
- short PA3 (RX) and PA2 (TX), making it a half-duplex UART (sufficient for testing most UARTs)
- connect PA2/PA3 to 150 Ohm in-line resistor R3 (used as protection to limit sink current)
- pull up R3 to 5V using 10 kOhm ressitor, this is necessary for the voltage shifter to operate up to 5V
- connect R3 to source of BSS138 n-channel MOSFET Q1, used as bi-directional level shifter
- connect gate of Q1 to Q2 drain
- connect source of Q1 to signal S
level shifter control, using BSS84 p-channel MOSFET Q2:
- gate: to PC14, pulled up to VTRG using 10-100 kOhm resistor
- source: VTRG
- drain: to signal S through 10 kOhm (this creates a pull-up resistor for the low side level shifter)
target voltage supply control, using BSS84 p-channel MOSFET Q3 (to provide 3.3V) and BSS138 n-channel MOSFET Q4 (to prevent feed back into 3.3V):
- Q3 gate: PC13, pulled up to 3.3V using 10-100 kOhm resistor
- Q3 source: 3.3V
- Q3 drain: Q4 source, 3.3V output for target
- Q4 source: Q3 drain, forward 3.3V to target (through body diode and because gate is on)
- Q4 gate: to 5V, enabling transition until 5V from target is present on drain
- Q4 drain: VTRG
all pins are configured using `define`s in the corresponding source code.
code
====
@ -81,15 +203,20 @@ The `bootloader` is started first and immediately jumps to the `application` if
The `application` image is the main application and is implemented in `application.c`.
It is up to the application to advertise USB DFU support (i.e. as does the provided USB CDC ACM example).
The `bootloader` image will be flashed using SWD (Serial Wire Debug).
The simplest way do flash the `bootloader` image is using the embedded bootloader.
By pressing the BOOT0 button (setting the pin low) while powering or resetting the device, the micro-controller boot its embedded UART/USB DFU bootloader.
Connect a USB cable and run `rake dfu_bootloader`.
Once the `bootloader` is flashed, it is possible to flash the `application` over USB using the DFU protocol by running `rake flash` (equivalent to `rake dfu_application`.
To force the bootloader to start the DFU mode press the user button or short a pin, depending on the board.
Note: I use my own DFU bootloader instead of the embedded bootloader because I was not able to start the embedded USB DFU bootloader from the application.
The images can also be flash using SWD (Serial Wire Debug) in case the firmware gets stuck and does not provide USB functionalities.
For that you need an SWD adapter.
The `Makefile` uses a ST-Link V2 programmer along OpenOCD software (default), or Black Magic Probe.
To flash the `booltoader` using SWD run `rake flash_booloader`.
If the development board uses the CKS32 chip STM32 alternative, use `CPUTAPID=0x2ba01477 rake flash_booloader`.
Once the `bootloader` is flashed it is possible to flash the `application` over USB using the DFU protocol by running `rake flash`.
To force the bootloader to start the DFU mode press the user button or short a pin, depending on the board.
It is also possible to flash the `application` image using SWD by running `rake flash_application`.
To flash the `bootloader` using SWD run `rake swd_bootloader` (this will also erase the application).
To flash the `application` using SWD run `rake swd_application` (or `rake swd`).
To erase all memory and unlock read/write protection, run `rake remove_protection`.
debug
-----
@ -100,4 +227,12 @@ To start the debugging session run `rake debug`.
USB
---
The firmware offers serial communication over USART1 and USB (using the CDC ACM device class).
The firmware offers serial communication over USB (using the CDC ACM device class).
TODO
====
add monitor single channel to support 1.8V voltage.
measure frequency of single channel.
decode single channel as RX.
probe UART using TX than RX.

View File

@ -1,8 +1,8 @@
# encoding: utf-8
# ruby: 2.4.2
=begin
Rakefile to manage compile CuVoodoo STM32F1 firmware.
the firmware is for development board based around a STM32F1xx micro-controller.
Rakefile to manage compiling CuVoodoo STM32F4 firmware.
the firmware is for development boards based around a STM32F4xx micro-controller.
the firmware uses the libopencm3 library providing support for this micro-controller.
=end
require 'rake'
@ -14,15 +14,26 @@ APPLICATION = "application"
FIRMWARES = [BOOTLOADER, APPLICATION]
# which development board is used
# supported are: SYSTEM_BOARD, MAPLE_MINI, BLUE_PILL, BLACK_PILL, CORE_BOARD, STLINKV2, BLASTER, BUSVOODOO
BOARD = ENV["BOARD"] || "BLUE_PILL"
# supported are: WeAct MiniF4 with STM32F401
BOARD = ENV["BOARD"] || "MINIF401"
# get MCU from board
DEVICE = case BOARD
when "MINIF401"
"stm32f401cc"
else
raise "unknown MCU for board #{BOARD}"
end
# libopencm3 definitions
LIBOPENCM3_DIR = "libopencm3"
LIBOPENCM3_INC = LIBOPENCM3_DIR+"/include"
LIBOPENCM3_LIB = LIBOPENCM3_DIR+"/lib"
# STM32F1 library used for this project provided by libopencm3
STM32F1_LIB = "opencm3_stm32f1"
LIBOPENCM3_LIBS = LIBOPENCM3_DIR+"/lib"
# get libopencm3
unless File.file?("./#{LIBOPENCM3_DIR}/scripts/genlink.py") then
sh "git submodule init"
sh "git submodule update"
end
LIBOPENCM3_LIB = "opencm3_" + `./#{LIBOPENCM3_DIR}/scripts/genlink.py #{LIBOPENCM3_DIR}/ld/devices.data #{DEVICE} FAMILY`
# source code used by the firmware
SRC_DIRS = [".", "lib"]
@ -43,7 +54,7 @@ cflags = [ENV["CFLAGS"]]
# optimize for size
cflags << "-Os"
# add debug symbols (remove for smaller release)
cflags << "-ggdb"
cflags << "-ggdb3"
# use C99 (supported by most an sufficient)
cflags << "-std=c99"
# have strict warning (for better code)
@ -59,7 +70,8 @@ cflags += SRC_DIRS.collect {|srd_dir| "-I #{srd_dir}"}
# include libopencm3 library
cflags << "-I #{LIBOPENCM3_INC}"
# add defines for micro-controller and board
cflags << "-DSTM32F1 -D#{BOARD}"
cflags << "-D#{BOARD}"
cflags << `./#{LIBOPENCM3_DIR}/scripts/genlink.py #{LIBOPENCM3_DIR}/ld/devices.data #{DEVICE} CPPFLAGS`
# render cflags
cflags = cflags.compact*' '
@ -76,14 +88,17 @@ ldflags_linker = ["--gc-sections"]
# show memory usage
ldflags_linker << "--print-memory-usage"
# add libopencm3 libraries
library_paths = [LIBOPENCM3_LIB]
library_paths = [LIBOPENCM3_LIBS]
# project libraries
ldlibs = [STM32F1_LIB]
ldlibs = [LIBOPENCM3_LIB]
# general libraries (gcc provides the ARM ABI)
ldlibs_linker = ["m", "c", "nosys", "gcc"]
# target micro-controller information (ARM Cortex-M3 supports thumb and thumb2, but does not include a floating point unit)
archflags = "-mthumb -mcpu=cortex-m3 -msoft-float"
# target micro-controller information (ARM Cortex-M4 supports thumb and thumb2, but does not include a floating point unit)
archflags = "-mthumb"
archflags += " -mcpu=" + `./#{LIBOPENCM3_DIR}/scripts/genlink.py #{LIBOPENCM3_DIR}/ld/devices.data #{DEVICE} CPU`
archflags += " -mfloat-abi=" + `./#{LIBOPENCM3_DIR}/scripts/genlink.py #{LIBOPENCM3_DIR}/ld/devices.data #{DEVICE} FPU`.split("-")[0]
archflags += " -mfpu=" + `./#{LIBOPENCM3_DIR}/scripts/genlink.py #{LIBOPENCM3_DIR}/ld/devices.data #{DEVICE} FPU`.split("-")[1..-1]*"-"
desc "compile firmwares"
task :default => FIRMWARES
@ -101,17 +116,17 @@ end
# get dependencies of a file
# done is a list of already known dependencies
def dependencies(source, done=[])
def dependencies(source, done = [])
d_path = source.ext("d") # get the dependency file
Rake::Task[d_path].invoke # ensure the dependency file exists
d_file = IO.read(d_path) # read the dependencies from dependency file
d_file = d_file.split(': ')[1].gsub("\n",'').gsub('\\ ','').gsub(/\s+/,' ').split(' ') # get a list of dependencies
d_file = d_file.split(': ')[1].gsub("\n", '').gsub('\\ ', '').gsub(/\s+/, ' ').split(' ') # get a list of dependencies
d_list = [] # list of dependencies
# only save dependencies which are in our source directories
d_file.each do |d|
SRC_DIRS.each do |dir|
if File.dirname(d)==dir then
d_list << d
if File.dirname(d) == dir then
d_list << d unless d.end_with?(".inc")
end
end
end
@ -127,14 +142,8 @@ def dependencies(source, done=[])
return done
end
desc "get libopencm3"
file LIBOPENCM3_DIR+"/Makefile" do
sh "git submodule init"
sh "git submodule update"
end
desc "compile libopencm3"
file "#{LIBOPENCM3_LIB}/lib#{STM32F1_LIB}.a" => LIBOPENCM3_DIR+"/Makefile" do
file "#{LIBOPENCM3_LIBS}/lib#{LIBOPENCM3_LIB}.a" do
sh "make --directory #{LIBOPENCM3_DIR}"
end
@ -143,17 +152,17 @@ task :doc => ["Doxyfile", "README.md"] do |t|
end
desc "compile source into object"
rule '.o' => ['.c', proc{|f| File.file?(f.ext("h")) ? f.ext("h") : []}, proc{|f| dependencies(f).collect{|d| File.file?(d.ext("h")) ? d.ext("h") : []}}, "#{LIBOPENCM3_LIB}/lib#{STM32F1_LIB}.a"] do |t|
rule '.o' => ['.c', proc{|f| File.file?(f.ext("h")) ? f.ext("h") : []}, proc{|f| dependencies(f).collect{|d| File.file?(d.ext("h")) ? d.ext("h") : []}}, "#{LIBOPENCM3_LIBS}/lib#{LIBOPENCM3_LIB}.a"] do |t|
sh "#{CC} #{cflags} #{archflags} -o #{t.name} -c #{t.prerequisites[0]}"
end
desc "generate dependencies"
rule '.d' => ['.c', "#{LIBOPENCM3_LIB}/lib#{STM32F1_LIB}.a"] do |t|
rule '.d' => ['.c', "#{LIBOPENCM3_LIBS}/lib#{LIBOPENCM3_LIB}.a"] do |t|
sh "#{CC} #{cflags} #{archflags} -MM -MF #{t.name} -c #{t.prerequisites[0]}"
end
desc "link binary"
rule '.elf' => ['.o', proc{|f| dependencies(f)}, '.ld', "#{LIBOPENCM3_LIB}/lib#{STM32F1_LIB}.a"] do |t|
rule '.elf' => ['.o', proc{|f| dependencies(f)}, '.ld', "#{LIBOPENCM3_LIBS}/lib#{LIBOPENCM3_LIB}.a"] do |t|
sh "#{LD} #{archflags} #{ldflags.join(' ')} #{t.prerequisites[0..-3].join(' ')} -T#{t.name.ext('ld')} #{ldflags_linker.collect{|flag| "-Wl,"+flag}.join(' ')} #{library_paths.collect{|path| "-L"+path}.join(' ')} #{ldlibs.collect{|lib| "-l"+lib}.join(' ')} -Wl,--start-group #{ldlibs_linker.collect{|lib| "-l"+lib}.join(' ')} -Wl,--end-group --output #{t.name}"
end
@ -190,42 +199,50 @@ OOCD = ENV["OOCD"] || "openocd"
# openOCD adapted name
OOCD_INTERFACE = ENV["OOCD_INTERFACE"] || (SWD_ADAPTER=="STLINKV2" ? "stlink" : "")
# openOCD target for the micro-controller
OOCD_TARGET = "stm32f1x"
OOCD_TARGET = "stm32f4x"
# Black Magic Probe port
BMP_PORT = ENV["BMP_PORT"] || "/dev/ttyACM0"
# set CPUTAPID (0x1ba01477 for STM32, 0x2ba01477 for CKS32/APM32)
CPUTAPID = ENV["CPUTAPID"] || "0x1ba01477"
desc "flash application"
task :flash => :dfu_application
desc "flash application using USB DFU"
task :flash => APPLICATION+".bin" do |t|
task :dfu_application => APPLICATION+".bin" do |t|
sh "dfu-util --device 1209:4356 --download #{t.source}"
end
desc "remove STM32F1 protection using SWD"
desc "flash application using USB DFU"
task :dfu_bootloader => BOOTLOADER+".bin" do |t|
sh "dfu-util --device 0483:df11 --cfg 1 --intf 0 --alt 0 --dfuse-address 0x08000000 --download #{t.source} --reset"
end
desc "remove STM32F4 protection using SWD"
task :remove_protection do
case SWD_ADAPTER
when "STLINKV2"
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --command 'set CPUTAPID #{CPUTAPID}' --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'halt' --command 'reset init' --command 'stm32f1x unlock 0' --command 'reset init' --command 'flash protect 0 0 last off' --command 'reset init' --command 'stm32f1x options_write 0 SWWDG NORSTSTNDBY NORSTSTOP' --command 'reset init' --command 'stm32f1x mass_erase 0' --command 'shutdown'"
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'halt' --command 'reset init' --command 'stm32f2x unlock 0' --command 'reset init' --command 'flash protect 0 0 last off' --command 'reset init' --command 'stm32f2x mass_erase 0' --command 'shutdown'"
when "BMP"
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='monitor option erase' --eval-command='monitor erase_mass' --eval-command='kill' --eval-command='quit'"
end
end
desc "flash bootloader using SWD"
task :flash_bootloader => BOOTLOADER+".hex" do |t|
task :swd_bootloader => BOOTLOADER+".hex" do |t|
case SWD_ADAPTER
when "STLINKV2"
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --command 'set CPUTAPID #{CPUTAPID}' --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'halt' --command 'reset init' --command 'flash erase_sector 0 0 last' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'halt' --command 'reset init' --command 'flash erase_sector 0 0 last' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
when "BMP"
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='monitor erase_mass' --eval-command='load' --eval-command='kill' --eval-command='quit' #{t.source}"
end
end
task :swd => :swd_application
desc "flash application using SWD"
task :flash_application => APPLICATION+".hex" do |t|
task :swd_application => APPLICATION+".hex" do |t|
case SWD_ADAPTER
when "STLINKV2"
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --command 'set CPUTAPID #{CPUTAPID}' --file target/#{OOCD_TARGET}.cfg --command 'adapter speed 100' --command 'init' --command 'halt' --command 'reset init' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --file target/#{OOCD_TARGET}.cfg --command 'adapter speed 100' --command 'init' --command 'halt' --command 'reset init' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
when "BMP"
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='load' --eval-command='kill' --eval-command='quit' #{t.source}"
end
@ -237,7 +254,7 @@ task :debug => APPLICATION+".elf" do |t|
case SWD_ADAPTER
when "STLINKV2"
# for GDB to work with openOCD the firmware needs to be reloaded
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command \"transport select hla_swd\" --command \"set CPUTAPID #{CPUTAPID}\" --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' #{t.source}")
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command \"transport select hla_swd\" --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' #{t.source}")
when "BMP"
exec("#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='monitor version' --eval-command='monitor swdp_scan' --eval-command='attach 1' #{t.source}")
end
@ -249,7 +266,7 @@ task :debug_bootloader => BOOTLOADER+".elf" do |t|
case SWD_ADAPTER
when "STLINKV2"
# for GDB to work with openOCD the firmware needs to be reloaded
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command \"transport select hla_swd\" --command \"set CPUTAPID #{CPUTAPID}\" --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' --eval-command='monitor reset init' #{t.source}")
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command \"transport select hla_swd\" --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' --eval-command='monitor reset init' #{t.source}")
when "BMP"
exec("#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='monitor version' --eval-command='monitor swdp_scan' --eval-command='attach 1' #{t.source}")
end

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,16 @@
/* linker script for application running on STM32F103x8 micro-controller
* the STM32F103x8 has 64 KB of flash starting at 0x0800 0000, and 20 KB of RAM starting at 0x2000 0000
* the STM32F103xB has 128 KB of flash starting at 0x0800 0000, and 20 KB of RAM starting at 0x2000 0000
* STM32F103x8 most often have if fact 128 KB, instead of the specified and advertised 64 KB, like the STM32F103xB
* you can define the desired flash size here.
* the USB DFU bootloader will take the first 8 KB of flash, followed by the application
/* linker script for application running on STM32F401xC micro-controller
* the STM32F401xC has 256 KB of flash starting at 0x0800 0000, and 64 KB of RAM starting at 0x2000 0000
* the USB DFU bootloader will use the first sector, which is 16 KB large.
* this is followed by the application.
* the first 4 bytes of the RAM is reserved for the DFU magic word (DFU! to start DFU bootloader)
*/
/* define memory regions. */
MEMORY
{
rom (rx) : ORIGIN = 0x08000000 + 8K, LENGTH = 64K - 8K
ram (rwx) : ORIGIN = 0x20000000 + 4, LENGTH = 20K - 4
rom (rx) : ORIGIN = 0x08000000 + 16K, LENGTH = 256K - 16K
ram (rwx) : ORIGIN = 0x20000000 + 4, LENGTH = 64K - 4
}
PROVIDE(__application_beginning = ORIGIN(rom));
/* if you want the firmware to use the flash size advertised by the micro-controller itself, use the following:
PROVIDE(__application_end = 0);
PROVIDE(__flash_end = 0);
if you want to enforce a flash size, because there is more flash than advertized by the micro-controller, use to following:
PROVIDE(__application_end = ORIGIN(rom) + LENGTH(rom));
PROVIDE(__flash_end = ORIGIN(rom) + LENGTH(rom));
*/
PROVIDE(__application_end = 0);
PROVIDE(__flash_end = 0);
/* RAM location reserved so application can talk to bootloader and tell to start DFU */
PROVIDE(__dfu_magic = ORIGIN(ram) - 4);

View File

@ -2,7 +2,7 @@
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2017-2019
* @date 2017-2020
*/
/* standard libraries */
#include <stdint.h> // standard integer types
@ -17,6 +17,11 @@
#include "global.h" // board definitions
#include "usb_dfu.h" // USB DFU utilities
/** symbol for beginning of the application
* @note this symbol will be provided by the bootloader linker script
*/
extern char __application_beginning;
/** bootloader entry point */
void main(void);
void main(void)
@ -31,38 +36,20 @@ void main(void)
__dfu_magic[1] = 0;
__dfu_magic[2] = 0;
__dfu_magic[3] = 0;
} else if (0 == (RCC_CSR & 0xfc000000)) { // no reset flag present -> this was a soft reset using scb_reset_core() after clearing the flags using RCC_CSR_RMVF, this was the legacy way to start the DFU mode
dfu_force = true;
} else { // check if the force DFU mode input is set
// disable SWJ pin to use as GPIO
#if (defined(DFU_FORCE_PIN) && defined(DFU_FORCE_VALUE))
#if ((GPIO(B) == GPIO_PORT(DFU_FORCE_PIN)) && (GPIO(4) == GPIO_PIN(DFU_FORCE_PIN)))
// JNTRST pin is used as DFU pin
rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternate function domain
gpio_primary_remap(AFIO_MAPR_SWJ_CFG_FULL_SWJ_NO_JNTRST, 0); // keep SWJ enable bit don't use JNTRST
#elif ((GPIO(B) == GPIO_PORT(DFU_FORCE_PIN)) && (GPIO(3) == GPIO_PIN(DFU_FORCE_PIN))) || ((GPIO(A) == GPIO_PORT(DFU_FORCE_PIN)) && (GPIO(15) == GPIO_PIN(DFU_FORCE_PIN)))
// JTAG but not SWD pin used as DFU pin
rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternate function domain
gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0); // disable JTAG but keep SWD
#elif ((GPIO(A) == GPIO_PORT(DFU_FORCE_PIN)) && (GPIO(14) == GPIO_PIN(DFU_FORCE_PIN))) || ((GPIO(A) == GPIO_PORT(DFU_FORCE_PIN)) && (GPIO(13) == GPIO_PIN(DFU_FORCE_PIN)))
// JTAG and SWD pin used as DFU pin
rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternate function domain
gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF, 0); // disable JTAG and SWD
#endif // DFU_FORCE_PIN
rcc_periph_clock_enable(GPIO_RCC(DFU_FORCE_PIN)); // enable clock for GPIO domain
gpio_set_mode(GPIO_PORT(DFU_FORCE_PIN), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_PIN(DFU_FORCE_PIN)); // set GPIO to input
// pull on the opposite of the expected value
rcc_periph_clock_enable(GPIO_RCC(DFU_FORCE_PIN)); // enable clock for button
#if (DFU_FORCE_VALUE == 1)
gpio_clear(GPIO_PORT(DFU_FORCE_PIN), GPIO_PIN(DFU_FORCE_PIN)); // pull down to be able to detect when tied to high
gpio_mode_setup(GPIO_PORT(DFU_FORCE_PIN), GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN(DFU_FORCE_PIN)); // set GPIO to input
if (gpio_get(GPIO_PORT(DFU_FORCE_PIN), GPIO_PIN(DFU_FORCE_PIN))) { // check if output is set to the value to force DFU mode
#else
gpio_set(GPIO_PORT(DFU_FORCE_PIN), GPIO_PIN(DFU_FORCE_PIN)); // pull up to be able to detect when tied to low
gpio_mode_setup(GPIO_PORT(DFU_FORCE_PIN), GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN(DFU_FORCE_PIN)); // set GPIO to input
if (0 == gpio_get(GPIO_PORT(DFU_FORCE_PIN), GPIO_PIN(DFU_FORCE_PIN))) { // check if output is set to the value to force DFU mode
#endif // DFU_FORCE_VALUE
dfu_force = true; // DFU mode forced
}
#endif // defined(DFU_FORCE_PIN)
rcc_periph_clock_disable(RCC_AFIO); // disable alternate function domain to put it back to default
rcc_periph_reset_pulse(GPIO_RST(DFU_FORCE_PIN)); // reset pin GPIO domain
rcc_periph_clock_disable(GPIO_RCC(DFU_FORCE_PIN)); // disable pin GPIO domain
}
@ -71,24 +58,20 @@ void main(void)
/* the application starts with the vector table
* the first entry in the vector table is the initial stack pointer (SP) address
* the stack will be placed in RAM
* on STM32F1xx SRAM begins at 0x2000 0000, and on STM32F103xx there is up to 96 KB of RAM (0x18000).
* since the stack grown "downwards" it should start at the end of the RAM: max 0x2001 8000
* on STM32F4 SRAM begins at 0x2000 0000, and on STM32F4xx there is up to 384 KiB of RAM (0x60000).
* since the stack grown "downwards" it should start at the end of the RAM: max 0x2006 0000
* if the SP is not in this range (e.g. flash has been erased) there is no valid application
* the second entry in the vector table is the reset address, corresponding to the application start
*/
volatile uint32_t* application = (uint32_t*)&__application_beginning; // get the value of the application address symbol (use a register instead on the stack since the stack pointer will be changed)
if (!dfu_force && (((*application) & 0xFFFE0000) == 0x20000000)) { // application at address seems valid
if (!dfu_force && (((*application) & 0xFFF80000) == 0x20000000)) { // application at address seems valid
SCB_VTOR = (volatile uint32_t)(application); // set vector table to application vector table (store at the beginning of the application)
__asm__ volatile ("MSR msp,%0" : :"r"(*application)); // set stack pointer to address provided in the beginning of the application (loaded into a register first)
(*(void(**)(void))((uint32_t)application + 4))(); // start application (by jumping to the reset function which address is stored as second entry of the vector table)
}
rcc_clock_setup_in_hse_8mhz_out_72mhz(); // start main clock
board_setup(); // setup board to control LED
led_on(); // indicate bootloader started
#if defined(BUSVOODOO)
led_toggle(); // switch from blue to red LED
#endif
usb_dfu_setup(); // setup USB DFU for firmware upload
usb_dfu_start(); // run DFU mode
}

View File

@ -1,28 +1,18 @@
/* linker script for application running on STM32F103x8 micro-controller
* the STM32F103x8 has 64 KB of flash starting at 0x0800 0000, and 20 KB of RAM starting at 0x2000 0000
* the STM32F103xB has 128 KB of flash starting at 0x0800 0000, and 20 KB of RAM starting at 0x2000 0000
* STM32F103x8 most often have if fact 128 KB, instead of the specified and advertised 64 KB, like the STM32F103xB
* you can define the desired flash size here.
* the USB DFU bootloader will take the first 8 KB of flash, followed by the application
/* linker script for application running on STM32F401xC micro-controller
* the STM32F401xC has 256 KB of flash starting at 0x0800 0000, and 64 KB of RAM starting at 0x2000 0000
* the USB DFU bootloader will use the first sector, which is 16 KB large.
* this is followed by the application.
* the first 4 bytes of the RAM is reserved for the DFU magic word (DFU! to start DFU bootloader)
*/
/* define memory regions. */
MEMORY
{
rom (rx) : ORIGIN = 0x08000000, LENGTH = 8K
ram (rwx) : ORIGIN = 0x20000000 + 4, LENGTH = 20K - 4
rom (rx) : ORIGIN = 0x08000000, LENGTH = 16K
ram (rwx) : ORIGIN = 0x20000000 + 4, LENGTH = 64K - 4
}
/* where the main application starts */
PROVIDE(__application_beginning = ORIGIN(rom) + LENGTH(rom));
/* if you want the firmware to use the flash size advertised by the micro-controller itself, use the following:
PROVIDE(__application_end = 0);
PROVIDE(__flash_end = 0);
if you want to enforce a flash size, because there is more flash than advertized by the micro-controller, use to following:
PROVIDE(__application_end = ORIGIN(rom) + LENGTH(rom));
PROVIDE(__flash_end = ORIGIN(rom) + LENGTH(rom));
*/
PROVIDE(__application_end = 0);
PROVIDE(__flash_end = 0);
/* RAM location reserved so application can talk to bootloader and tell to start DFU */
PROVIDE(__dfu_magic = ORIGIN(ram) - 4);

110
global.c
View File

@ -16,6 +16,8 @@
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/exti.h> // external interrupt defines
#include <libopencm3/stm32/syscfg.h> // system definitions
#include <libopencm3/usb/dwc/otg_fs.h> // USB OTG utilities
#include "global.h" // common methods
@ -132,9 +134,6 @@ char* b2s(uint64_t binary, uint8_t rjust)
inline void led_on(void)
{
#if defined(LED_PIN)
#if defined(BUSVOODOO)
gpio_set_mode(GPIO_PORT(LED_PIN), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LED_PIN)); // set LED pin push-pull
#endif // BUSVOODOO
#if defined(LED_ON) && LED_ON
gpio_set(GPIO_PORT(LED_PIN), GPIO_PIN(LED_PIN));
#else
@ -147,14 +146,10 @@ inline void led_on(void)
inline void led_off(void)
{
#if defined(LED_PIN)
#if defined(BUSVOODOO)
gpio_set_mode(GPIO_PORT(LED_PIN), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LED_PIN)); // set LED pin to floating to disable LEDs
#else
#if defined(LED_ON) && LED_ON
gpio_clear(GPIO_PORT(LED_PIN), GPIO_PIN(LED_PIN));
#else
gpio_set(GPIO_PORT(LED_PIN), GPIO_PIN(LED_PIN));
#endif // BUSVOODOO
#endif // LED_ON
#endif // LED_PIN
}
@ -163,21 +158,18 @@ inline void led_off(void)
inline void led_toggle(void)
{
#if defined(LED_PIN)
#if defined(BUSVOODOO)
gpio_set_mode(GPIO_PORT(LED_PIN), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LED_PIN)); // set LED pin to push-pull
#endif // BUSVOODOO
gpio_toggle(GPIO_PORT(LED_PIN), GPIO_PIN(LED_PIN));
#endif // LED_PIN
}
void sleep_us(uint32_t duration)
{
if (duration <= 5) { // less than the setup time
for (volatile uint32_t nop = 0; nop < 5 * duration; nop++); // busy loop, approximate, hand tuned for 72 MHz system clock
if (duration <= 4) { // less than the setup time
for (volatile uint32_t nop = 0; nop < 5 * duration; nop++); // busy loop, approximate
return;
}
duration -= 5; // subtract setup time
duration -= 4; // subtract setup time
systick_counter_disable(); // disable SysTick to reconfigure it
if (!systick_set_frequency(1000000, rcc_ahb_frequency)) { // set SysTick frequency to microseconds
while (true); // unhandled error
@ -249,33 +241,41 @@ void user_input_store(char c)
void board_setup(void)
{
#if defined(LED_PIN)
// setup main clock
#if defined(MINIF401)
rcc_clock_setup_pll(&rcc_hse_25mhz_3v3[RCC_CLOCK_3V3_84MHZ]); // the MINIF401 uses an STM32F401 which can go up to 84 MHz, and the board has a 25 MHz crystal
#else
rcc_clock_setup_pll(&rcc_hsi_configs[RCC_CLOCK_3V3_84MHZ]); // use HSI which is present on all boards, and limit to 84MHz (supported by all STM32F4
#endif
#if defined(LED_PIN) && defined(LED_ON)
// setup LED
rcc_periph_clock_enable(GPIO_RCC(LED_PIN)); // enable clock for LED
#if defined(BUSVOODOO)
gpio_set_mode(GPIO_PORT(LED_PIN), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LED_PIN)); // set LED pin to floating to disable LEDs
#else
gpio_set_mode(GPIO_PORT(LED_PIN), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LED_PIN)); // set LED pin to output push-pull do drive LED
gpio_mode_setup(GPIO_PORT(LED_PIN), GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN(LED_PIN)); // set LED pin as output
#if LED_ON // LED is on when sourcing
gpio_set_output_options(GPIO_PORT(LED_PIN), GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, GPIO_PIN(LED_PIN)); // set LED pin output as push-pull
#else // LED is on when sinking
gpio_set_output_options(GPIO_PORT(LED_PIN), GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO_PIN(LED_PIN)); // set LED pin output as open-drain
#endif
led_off(); // switch off LED per default
#endif // LED_PIN
// setup button
#if defined(BUTTON_PIN)
/*
#if defined(BUTTON_PIN) && defined(BUTTON_PRESSED)
rcc_periph_clock_enable(GPIO_RCC(BUTTON_PIN)); // enable clock for button
gpio_set_mode(GPIO_PORT(BUTTON_PIN), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_PIN(BUTTON_PIN)); // set button pin to input
rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
exti_select_source(GPIO_EXTI(BUTTON_PIN), GPIO_PORT(BUTTON_PIN)); // mask external interrupt of this pin only for this port
#if defined(BUTTON_PRESSED) && BUTTON_PRESSED
gpio_clear(GPIO_PORT(BUTTON_PIN), GPIO_PIN(BUTTON_PIN)); // pull down to be able to detect button push (go high)
#if BUTTON_PRESSED // level goes high when pressed
gpio_mode_setup(GPIO_PORT(BUTTON_PIN), GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN(BUTTON_PIN)); // set GPIO to input and pull down
exti_set_trigger(GPIO_EXTI(BUTTON_PIN), EXTI_TRIGGER_RISING); // trigger when button is pressed
#else
gpio_set(GPIO_PORT(BUTTON_PIN), GPIO_PIN(BUTTON_PIN)); // pull up to be able to detect button push (go low)
#else // level goes low when pressed
gpio_mode_setup(GPIO_PORT(BUTTON_PIN), GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN(BUTTON_PIN)); // set GPIO to input and pull up
exti_set_trigger(GPIO_EXTI(BUTTON_PIN), EXTI_TRIGGER_FALLING); // trigger when button is pressed
#endif
exti_enable_request(GPIO_EXTI(BUTTON_PIN)); // enable external interrupt
nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(BUTTON_PIN)); // enable interrupt
#endif
*/
// reset user input buffer
user_input_available = false;
@ -283,6 +283,66 @@ void board_setup(void)
user_input_used = 0;
}
/** disconnect USB by sending a reset condition */
static void usb_disconnect(void)
{
if (OTG_FS_GUSBCFG & OTG_GUSBCFG_FDMOD) { // USB configured as device
// pull USB D+ low for a short while
OTG_FS_DCTL |= OTG_DCTL_SDIS; // disconnect DP pull-up to simulate a disconnect
// in case there is an external pull-up resistor, pull DP low
// I have no idea why, but once USB is configured, I can't use PA12/DP back as GPIO
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO12); // be sure the D+ pin can be used as GPIO output
gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, GPIO12); // use push-pull output
gpio_clear(GPIOA, GPIO12); // pull D+ low
for (volatile uint32_t i = 0; i < 0x2000; i++); // USB disconnected must be at least 10 ms long, at most 100 ms
}
}
void system_memory(void)
{
usb_disconnect(); // disconnect from USB (if necessary)
// for more details, see https://stm32f4-discovery.net/2017/04/tutorial-jump-system-memory-software-stm32/
// deinit RCC (according to STM32CubeF4 source code)
RCC_CR |= RCC_CR_HSION; // enable high speed internal clock
while (!(RCC_CR & RCC_CR_HSIRDY)); // wait until clock is ready
RCC_CR |= (0x10U << RCC_CR_HSITRIM_SHIFT); // set HSITRIM[4:0] bits to the reset value
RCC_CFGR = 0;// reset CFGR register
while (((RCC_CFGR >> RCC_CFGR_SWS_SHIFT) & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_HSI); // wait it till clock switch is ready
RCC_CR &= ~(RCC_CR_HSEON | RCC_CR_HSEBYP | RCC_CR_CSSON); // clear HSEON, HSEBYP and CSSON bits
while (RCC_CR & RCC_CR_HSERDY); // wait till HSE is disabled
RCC_CR &= ~RCC_CR_PLLON; // Clear PLLON bit
while (RCC_CR & RCC_CR_PLLRDY); // wait till PLL is disabled
//RCC_PLLCFGR = 0x24003010; // reset PLLCFGR register to default value (value for STM32F401)
RCC_CIR &= ~(RCC_CIR_LSIRDYIE | RCC_CIR_LSERDYIE | RCC_CIR_HSIRDYIE | RCC_CIR_HSERDYIE | RCC_CIR_PLLRDYIE); // disable all interrupts
RCC_CIR |= (RCC_CIR_LSIRDYC | RCC_CIR_LSERDYC | RCC_CIR_HSIRDYC | RCC_CIR_HSERDYC | RCC_CIR_PLLRDYC | RCC_CIR_CSSC); // clear all interrupt flags
RCC_CR &= ~RCC_CSR_LSION; // clear LSION bit
RCC_CSR |= RCC_CSR_RMVF; // reset all CSR flags
// switch to system memory
RCC_APB2ENR = RCC_APB2ENR_SYSCFGEN; // enable system configure clock (all others are not required)
cm_disable_interrupts(); // disable all interrupts
SYSCFG_MEMRM = 1; // map system memory to 0x0000 0000 (this bypasses the BOOT0 pin)
const uint32_t address = 0x1FFF0000; // system memory address
__asm__ volatile ("MSR msp,%0" : :"r"(*(uint32_t*)address)); // set stack pointer to address provided in the beginning of the bootloader (loaded into a register first)
(*(void(**)(void))((uint32_t)address + 4))(); // start system memory (by jumping to the reset function which address is stored as second entry of the vector table)
// we should not reach this point
}
void dfu_bootloader(void)
{
usb_disconnect(); // disconnect from USB (if necessary)
// set DFU magic to specific RAM location
__dfu_magic[0] = 'D';
__dfu_magic[1] = 'F';
__dfu_magic[2] = 'U';
__dfu_magic[3] = '!';
scb_reset_system(); // reset system (core and peripherals)
while (true); // wait for the reset to happen
}
#if defined(BUTTON_PIN)
/** interrupt service routine called when button is pressed */
void GPIO_EXTI_ISR(BUTTON_PIN)(void)

329
global.h
View File

@ -7,7 +7,12 @@
#pragma once
/** enable debugging functionalities */
#define DEBUG true
#define DEBUG false
/** Data Synchronization Barrier, ensure all data is written */
#define __DSB() __asm__("dsb")
/** Instruction Synchronization Barrier, ensure all instruction are run */
#define __ISB() __asm__("isb")
/** get the length of an array */
#define LENGTH(x) (sizeof(x) / sizeof((x)[0]))
@ -385,37 +390,20 @@ int32_t adds32_safe(int32_t a, int32_t b);
#define RST_TIM(x) CAT2(RST_TIM,x)
/** get NVIC IRQ for timer base on TIM identifier */
#define NVIC_TIM_IRQ(x) CAT3(NVIC_TIM,x,_IRQ)
/** NVIC IRQ for timer 9 */
#define NVIC_TIM9_IRQ NVIC_TIM1_BRK_TIM9_IRQ
/** NVIC IRQ for timer 10 */
#define NVIC_TIM10_IRQ NVIC_TIM1_UP_TIM10_IRQ
/** NVIC IRQ for timer 11 */
#define NVIC_TIM11_IRQ NVIC_TIM1_TRG_COM_TIM11_IRQ
/** get interrupt service routine for timer base on TIM identifier */
#define TIM_ISR(x) CAT3(tim,x,_isr)
/** get port based on TIMx_CHy identifier */
#define TIM_CH_PORT(x,y) CAT4(GPIO_BANK_TIM,x,_CH,y)
/** get pin based on TIMx_CHy identifier */
#define TIM_CH_PIN(x,y) CAT4(GPIO_TIM,x,_CH,y)
/** get RCC for port based on TIMx_CHy identifier */
#define RCC_TIM_CH(x,y) CAT4(RCC_TIM,x,_CH,y)
#define RCC_TIM1_CH1 RCC_GPIOA /**< RCC for port for on TIM1_CH1 */
#define RCC_TIM1_CH2 RCC_GPIOA /**< RCC for port for on TIM1_CH2 */
#define RCC_TIM1_CH3 RCC_GPIOA /**< RCC for port for on TIM1_CH3 */
#define RCC_TIM1_CH4 RCC_GPIOA /**< RCC for port for on TIM1_CH4 */
#define RCC_TIM1_CH1N RCC_GPIOB /**< RCC for port for on TIM1_CH1N */
#define RCC_TIM1_CH2N RCC_GPIOB /**< RCC for port for on TIM1_CH2N */
#define RCC_TIM1_CH3N RCC_GPIOB /**< RCC for port for on TIM1_CH3N */
#define RCC_TIM2_CH1_ETR RCC_GPIOA /**< RCC for port for on TIM2_CH1_ETR */
#define RCC_TIM2_CH2 RCC_GPIOA /**< RCC for port for on TIM2_CH2 */
#define RCC_TIM2_CH3 RCC_GPIOA /**< RCC for port for on TIM2_CH3 */
#define RCC_TIM2_CH4 RCC_GPIOA /**< RCC for port for on TIM2_CH4 */
#define RCC_TIM3_CH1 RCC_GPIOA /**< RCC for port for on TIM3_CH1 */
#define RCC_TIM3_CH2 RCC_GPIOA /**< RCC for port for on TIM3_CH2 */
#define RCC_TIM3_CH3 RCC_GPIOB /**< RCC for port for on TIM3_CH3 */
#define RCC_TIM3_CH4 RCC_GPIOB /**< RCC for port for on TIM3_CH4 */
#define RCC_TIM4_CH1 RCC_GPIOB /**< RCC for port for on TIM4_CH1 */
#define RCC_TIM4_CH2 RCC_GPIOB /**< RCC for port for on TIM4_CH2 */
#define RCC_TIM4_CH3 RCC_GPIOB /**< RCC for port for on TIM4_CH3 */
#define RCC_TIM4_CH4 RCC_GPIOB /**< RCC for port for on TIM4_CH4 */
#define RCC_TIM5_CH1 RCC_GPIOA /**< RCC for port for on TIM5_CH1 */
#define RCC_TIM5_CH2 RCC_GPIOA /**< RCC for port for on TIM5_CH2 */
#define RCC_TIM5_CH3 RCC_GPIOA /**< RCC for port for on TIM5_CH3 */
#define RCC_TIM5_CH4 RCC_GPIOA /**< RCC for port for on TIM5_CH4 */
/** interrupt service routine for timer 9 */
#define tim9_isr tim1_brk_tim9_isr
/** interrupt service routine for timer 10 */
#define tim10_isr tim1_up_tim10_isr
/** interrupt service routine for timer 11 */
#define tim11_isr tim1_trg_com_tim11_isr
/** get TIM_IC based on CHx identifier */
#define TIM_IC(x) CAT2(TIM_IC,x)
/** get TIM_IC_IN_TI based on CHx identifier */
@ -472,123 +460,12 @@ int32_t adds32_safe(int32_t a, int32_t b);
#define USART_IRQ(x) CAT3(NVIC_USART,x,_IRQ)
/** get interrupt service routine for USART based on USART identifier */
#define USART_ISR(x) CAT3(usart,x,_isr)
/** get port for USART transmit pin based on USART identifier */
#define USART_TX_PORT(x) CAT3(GPIO_BANK_USART,x,_TX)
/** get port for USART receive pin based on USART identifier */
#define USART_RX_PORT(x) CAT3(GPIO_BANK_USART,x,_RX)
/** get port for USART RTS pin based on USART identifier */
#define USART_RTS_PORT(x) CAT3(GPIO_BANK_USART,x,_RTS)
/** get port for USART CTS pin based on USART identifier */
#define USART_CTS_PORT(x) CAT3(GPIO_BANK_USART,x,_CTS)
/** get pin for USART transmit pin based on USART identifier */
#define USART_TX_PIN(x) CAT3(GPIO_USART,x,_TX)
/** get pin for USART receive pin based on USART identifier */
#define USART_RX_PIN(x) CAT3(GPIO_USART,x,_RX)
/** get pin for USART RTS pin based on USART identifier */
#define USART_RTS_PIN(x) CAT3(GPIO_USART,x,_RTS)
/** get pin for USART CTS pin based on USART identifier */
#define USART_CTS_PIN(x) CAT3(GPIO_USART,x,_CTS)
/** get RCC for USART port based on USART identifier */
#define RCC_USART_PORT(x) CAT2(RCC_USART_PORT,x)
#define RCC_USART_PORT1 RCC_GPIOA /**< USART 1 is on port A */
#define RCC_USART_PORT2 RCC_GPIOA /**< USART 2 is on port A */
#define RCC_USART_PORT3 RCC_GPIOB /**< USART 3 is on port B */
/** get port based on ADC12_IN identifier */
#define ADC12_IN_PORT(x) CAT3(ADC12_IN,x,_PORT)
#define ADC12_IN0_PORT GPIOA /**< ADC12_IN0 is on PA0 */
#define ADC12_IN1_PORT GPIOA /**< ADC12_IN1 is on PA1 */
#define ADC12_IN2_PORT GPIOA /**< ADC12_IN2 is on PA2 */
#define ADC12_IN3_PORT GPIOA /**< ADC12_IN3 is on PA3 */
#define ADC12_IN4_PORT GPIOA /**< ADC12_IN4 is on PA4 */
#define ADC12_IN5_PORT GPIOA /**< ADC12_IN5 is on PA5 */
#define ADC12_IN6_PORT GPIOA /**< ADC12_IN6 is on PA6 */
#define ADC12_IN7_PORT GPIOA /**< ADC12_IN7 is on PA7 */
#define ADC12_IN8_PORT GPIOB /**< ADC12_IN8 is on PB0 */
#define ADC12_IN9_PORT GPIOB /**< ADC12_IN9 is on PB1 */
#define ADC12_IN10_PORT GPIOC /**< ADC12_IN10 is on PC0 */
#define ADC12_IN11_PORT GPIOC /**< ADC12_IN11 is on PC1 */
#define ADC12_IN12_PORT GPIOC /**< ADC12_IN12 is on PC2 */
#define ADC12_IN13_PORT GPIOC /**< ADC12_IN13 is on PC3 */
#define ADC12_IN14_PORT GPIOC /**< ADC12_IN14 is on PC4 */
#define ADC12_IN15_PORT GPIOC /**< ADC12_IN15 is on PC5 */
/** get pin based on ADC12_IN identifier */
#define ADC12_IN_PIN(x) CAT3(ADC12_IN,x,_PIN)
#define ADC12_IN0_PIN GPIO0 /**< ADC12_IN0 is on PA0 */
#define ADC12_IN1_PIN GPIO1 /**< ADC12_IN1 is on PA1 */
#define ADC12_IN2_PIN GPIO2 /**< ADC12_IN2 is on PA2 */
#define ADC12_IN3_PIN GPIO3 /**< ADC12_IN3 is on PA3 */
#define ADC12_IN4_PIN GPIO4 /**< ADC12_IN4 is on PA4 */
#define ADC12_IN5_PIN GPIO5 /**< ADC12_IN5 is on PA5 */
#define ADC12_IN6_PIN GPIO6 /**< ADC12_IN6 is on PA6 */
#define ADC12_IN7_PIN GPIO7 /**< ADC12_IN7 is on PA7 */
#define ADC12_IN8_PIN GPIO0 /**< ADC12_IN8 is on PB0 */
#define ADC12_IN9_PIN GPIO1 /**< ADC12_IN9 is on PB1 */
#define ADC12_IN10_PIN GPIO0 /**< ADC12_IN10 is on PC0 */
#define ADC12_IN11_PIN GPIO1 /**< ADC12_IN11 is on PC1 */
#define ADC12_IN12_PIN GPIO2 /**< ADC12_IN12 is on PC2 */
#define ADC12_IN13_PIN GPIO3 /**< ADC12_IN13 is on PC3 */
#define ADC12_IN14_PIN GPIO4 /**< ADC12_IN14 is on PC4 */
#define ADC12_IN15_PIN GPIO5 /**< ADC12_IN15 is on PC5 */
/** get RCC based on ADC12_IN identifier */
#define RCC_ADC12_IN(x) CAT2(RCC_ADC12_IN,x)
#define RCC_ADC12_IN0 RCC_GPIOA /**< ADC12_IN0 is on PA0 */
#define RCC_ADC12_IN1 RCC_GPIOA /**< ADC12_IN1 is on PA1 */
#define RCC_ADC12_IN2 RCC_GPIOA /**< ADC12_IN2 is on PA2 */
#define RCC_ADC12_IN3 RCC_GPIOA /**< ADC12_IN3 is on PA3 */
#define RCC_ADC12_IN4 RCC_GPIOA /**< ADC12_IN4 is on PA4 */
#define RCC_ADC12_IN5 RCC_GPIOA /**< ADC12_IN5 is on PA5 */
#define RCC_ADC12_IN6 RCC_GPIOA /**< ADC12_IN6 is on PA6 */
#define RCC_ADC12_IN7 RCC_GPIOA /**< ADC12_IN7 is on PA7 */
#define RCC_ADC12_IN8 RCC_GPIOB /**< ADC12_IN8 is on PB0 */
#define RCC_ADC12_IN9 RCC_GPIOB /**< ADC12_IN9 is on PB1 */
#define RCC_ADC12_IN10 RCC_GPIOC /**< ADC12_IN10 is on PC0 */
#define RCC_ADC12_IN11 RCC_GPIOC /**< ADC12_IN11 is on PC1 */
#define RCC_ADC12_IN12 RCC_GPIOC /**< ADC12_IN12 is on PC2 */
#define RCC_ADC12_IN13 RCC_GPIOC /**< ADC12_IN13 is on PC3 */
#define RCC_ADC12_IN14 RCC_GPIOC /**< ADC12_IN14 is on PC4 */
#define RCC_ADC12_IN15 RCC_GPIOC /**< ADC12_IN15 is on PC5 */
/** get channel based on ADC12_IN identifier */
/** get channel based on ADC1_IN identifier */
#define ADC_CHANNEL(x) CAT2(ADC_CHANNEL,x)
/** get SPI based on SPI identifier */
#define SPI(x) CAT2(SPI,x)
/** get RCC for SPI based on SPI identifier */
#define RCC_SPI(x) CAT2(RCC_SPI,x)
/** get RCC for GPIO port for SPI NSS signals */
#define RCC_SPI_NSS_PORT(x) CAT3(RCC_SPI,x,_NSS_PORT)
#define RCC_SPI1_NSS_PORT RCC_GPIOA /**< RCC for GPIO port for NSS for SPI1 */
#define RCC_SPI1_RE_NSS_PORT RCC_GPIOA /**< RCC for GPIO port for NSS for SPI1_RE */
#define RCC_SPI2_NSS_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI2 */
/** get RCC for GPIO port for SPI SCK signals */
#define RCC_SPI_SCK_PORT(x) CAT3(RCC_SPI,x,_SCK_PORT)
#define RCC_SPI1_SCK_PORT RCC_GPIOA /**< RCC for GPIO port for NSS for SPI1 */
#define RCC_SPI1_RE_SCK_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI1_RE */
#define RCC_SPI2_SCK_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI2 */
/** get RCC for GPIO port for SPI MISO signals */
#define RCC_SPI_MISO_PORT(x) CAT3(RCC_SPI,x,_MISO_PORT)
#define RCC_SPI1_MISO_PORT RCC_GPIOA /**< RCC for GPIO port for NSS for SPI1 */
#define RCC_SPI1_RE_MISO_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI1_RE */
#define RCC_SPI2_MISO_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI2 */
/** get RCC for GPIO port for SPI MOSI signals */
#define RCC_SPI_MOSI_PORT(x) CAT3(RCC_SPI,x,_MOSI_PORT)
#define RCC_SPI1_MOSI_PORT RCC_GPIOA /**< RCC for GPIO port for NSS for SPI1 */
#define RCC_SPI1_RE_MOSI_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI1_RE */
#define RCC_SPI2_MOSI_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI2 */
/** get SPI port for NSS signal based on SPI identifier */
#define SPI_NSS_PORT(x) CAT3(GPIO_BANK_SPI,x,_NSS)
/** get SPI port for SCK signal based on SPI identifier */
#define SPI_SCK_PORT(x) CAT3(GPIO_BANK_SPI,x,_SCK)
/** get SPI port for MISO signal based on SPI identifier */
#define SPI_MISO_PORT(x) CAT3(GPIO_BANK_SPI,x,_MISO)
/** get SPI port for MOSI signal based on SPI identifier */
#define SPI_MOSI_PORT(x) CAT3(GPIO_BANK_SPI,x,_MOSI)
/** get SPI pin for NSS signal based on SPI identifier */
#define SPI_NSS_PIN(x) CAT3(GPIO_SPI,x,_NSS)
/** get SPI pin for SCK signal based on SPI identifier */
#define SPI_SCK_PIN(x) CAT3(GPIO_SPI,x,_SCK)
/** get SPI pin for MISO signal based on SPI identifier */
#define SPI_MISO_PIN(x) CAT3(GPIO_SPI,x,_MISO)
/** get SPI pin for MOSI signal based on SPI identifier */
#define SPI_MOSI_PIN(x) CAT3(GPIO_SPI,x,_MOSI)
/** get SPI CRC polynomial register based on SPI identifier */
#define SPI_CRC_PR(x) CAT3(SPI,x,_CRCPR)
/** get SPI CRC transmit register based on SPI identifier */
@ -599,86 +476,82 @@ int32_t adds32_safe(int32_t a, int32_t b);
#define SPI_IRQ(x) CAT3(NVIC_SPI,x,_IRQ)
/** get SPI ISR based on SPI identifier */
#define SPI_ISR(x) CAT3(spi,x,_isr)
/** get DMA based on SPI identifier */
#define DMA_SPI(x) CAT2(DMA_SPI,x)
#define DMA_SPI1 DMA1 /**< SPI1 is on DMA1 */
#define DMA_SPI2 DMA1 /**< SPI2 is on DMA1 */