├── .cargo └── config.toml ├── .github ├── bors.toml └── workflows │ ├── changelog.yml │ ├── ci.yml │ ├── clippy.yml │ └── rustfmt.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── Cargo.toml ├── Embed.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── PY32F0_Series.yaml ├── README.md ├── examples ├── adc_values.rs ├── blinky.rs ├── blinky_adc.rs ├── blinky_delay.rs ├── blinky_multiple.rs ├── blinky_rtc.rs ├── blinky_timer.rs ├── blinky_timer_irq.rs ├── exti.rs ├── flash_systick.rs ├── flash_systick_fancier.rs ├── i2c_find_address.rs ├── led_hal_button_irq.rs ├── pwm.rs ├── pwm_complementary.rs ├── serial_echo.rs ├── serial_spi_bridge.rs ├── serial_stopwatch.rs ├── spi_hal_apa102c.rs ├── spi_rc522.rs └── watchdog.rs ├── memory.x ├── openocd.cfg ├── openocd_program.sh ├── src ├── adc.rs ├── dma.rs ├── gpio.rs ├── gpio │ ├── erased.rs │ ├── hal_02.rs │ ├── hal_1.rs │ └── partially_erased.rs ├── i2c.rs ├── lib.rs ├── prelude.rs ├── rcc.rs ├── rcc │ └── enable.rs ├── rtc.rs ├── serial.rs ├── serial │ ├── hal_02.rs │ └── hal_1.rs ├── spi.rs ├── spi │ ├── dma.rs │ ├── hal_02.rs │ └── hal_1.rs ├── time.rs ├── timer.rs ├── timer │ ├── counter.rs │ ├── delay.rs │ ├── hal_02.rs │ ├── hal_1.rs │ ├── monotonic.rs │ ├── pins.rs │ └── pwm.rs └── watchdog.rs └── tools ├── capture_example_bloat.sh ├── capture_nightly_example_bloat.sh └── check.py /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.thumbv6m-none-eabi] 2 | runner = "probe-rs run --chip PY32F002Ax5" 3 | rustflags = [ 4 | "-C", 5 | "link-arg=-Tlink.x", 6 | "-C", 7 | "link-arg=--nmagic", 8 | 9 | # --- Defmt link script --- 10 | "-C", 11 | "link-arg=-Tdefmt.x", 12 | ] 13 | 14 | [build] 15 | target = "thumbv6m-none-eabi" 16 | 17 | [env] 18 | DEFMT_LOG = "trace" 19 | -------------------------------------------------------------------------------- /.github/bors.toml: -------------------------------------------------------------------------------- 1 | required_approvals = 1 2 | block_labels = ["wip"] 3 | delete_merged_branches = true 4 | status = [ 5 | "Rustfmt", 6 | "ci (stable)", 7 | ] 8 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request_target: 3 | 4 | name: Changelog check 5 | 6 | jobs: 7 | changelog: 8 | name: Changelog check 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout sources 12 | uses: actions/checkout@v3 13 | 14 | - name: Changelog updated 15 | uses: Zomzog/changelog-checker@v1.2.0 16 | with: 17 | fileName: CHANGELOG.md 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ staging, trying, master ] 4 | pull_request: 5 | merge_group: 6 | 7 | name: Continuous integration 8 | 9 | jobs: 10 | ci: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | rust: 15 | - stable 16 | include: 17 | - rust: nightly 18 | experimental: true 19 | 20 | steps: 21 | - name: Checkout code 22 | uses: actions/checkout@v3 23 | 24 | - name: Install Rust 25 | uses: actions-rs/toolchain@v1 26 | with: 27 | profile: minimal 28 | toolchain: ${{ matrix.rust }} 29 | target: thumbv6m-none-eabi 30 | override: true 31 | 32 | - name: Regular build 33 | run: python tools/check.py 34 | 35 | - name: Size check 36 | run: python tools/check.py size_check 37 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ staging, trying, master ] 4 | pull_request: 5 | 6 | name: Clippy check 7 | jobs: 8 | clippy_check: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - run: rustup component add clippy 13 | - uses: actions-rs/toolchain@v1 14 | with: 15 | toolchain: stable 16 | target: thumbv6m-none-eabi 17 | override: true 18 | - uses: actions-rs/clippy-check@v1 19 | with: 20 | token: ${{ secrets.GITHUB_TOKEN }} 21 | args: --features=py32f072 --target thumbv6m-none-eabi 22 | -------------------------------------------------------------------------------- /.github/workflows/rustfmt.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ staging, trying, master ] 4 | pull_request: 5 | 6 | name: Code formatting check 7 | 8 | jobs: 9 | fmt: 10 | name: Rustfmt 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions-rs/toolchain@v1 15 | with: 16 | profile: minimal 17 | toolchain: stable 18 | override: true 19 | - run: rustup component add rustfmt 20 | - uses: actions-rs/cargo@v1 21 | with: 22 | command: fmt 23 | args: --all -- --check 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.orig 3 | **/*.rs.bk 4 | Cargo.lock 5 | *.txt 6 | .vscode/launch.json -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.features": ["py32f002ax5"], 3 | // "rust-analyzer.showUnlinkedFileNotification": false, 4 | "rust-analyzer.check.allTargets": false 5 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## v0.3.1 - 2025-03-08 6 | 7 | ### Fixed 8 | 9 | - Bug in gpio, _is_low(), was returning false if low, true if high, now correct 10 | 11 | ## v0.3.0 - 2025-02-14 12 | 13 | ### Added 14 | 15 | - `timer` module trait:`WithPwm` and enum:`Channel` 16 | - implemented `WithPwm` trait on hardware timers 17 | - timer module `pwm` that implements pwm using new code 18 | - implemented embedded-hal pwm traits 19 | - added `crate::timer::pwm::PwmExt` trait to prelude 20 | 21 | ### Changed 22 | 23 | - This version requires v0.2.1 of the [py32-rs](https://github.com/py32-rust/py32-rs.git) pac crate. 24 | - Updated pwm examples to use new pwm code 25 | 26 | ### Removed 27 | 28 | - Removed BUGBUG comment in gpio.rs 29 | - Removed allow unsafe directive in rtc.rs 30 | - Removed `pwm` separate module, reimplemented in `timer` 31 | - Removed use of `bitflags` crate in timer.rs 32 | - Removed `embedded-time` from Cargo.toml 33 | 34 | ### Fixed 35 | 36 | - Checked against datasheets and corrected I2C pins in `i2c_pins` macro 37 | - Fixed warning in rcc.rs 38 | - Fixed array register call in monotonic.rs 39 | 40 | ## v0.2.2 - 2025-02-07 41 | 42 | ### Changed 43 | 44 | - Modified license from BSD to dual MIT/Apache 45 | - Added peripheral coverage in README.md 46 | 47 | ## v0.2.1 - 2025-02-04 48 | 49 | ### Fixed 50 | 51 | - Fixed bug with gpio mode setting for output push pull vs open drain, causing hardware resets. 52 | 53 | ## v0.2.0 - 2025-01-21 54 | 55 | This version depends on py32-rs v0.2.0 or later 56 | 57 | ### Added 58 | 59 | - added optional feature "rtic" to implement `rtic-monotonic::Monotonic` trait on timers. 60 | - added optional feature "defmt" to implement `defmt::Format` trait on Errors in various modules 61 | - added feature "with-dma" for internal use to simplify dma related code 62 | - added module `dma` 63 | - module `gpio` has the following new types and traits: 64 | * Struct `PartiallyErasedPin` and `ErasedPin` replaces previous `Pin` 65 | * Struct `DynamicPin` allows pin to swap between an `Input` and `Output` pin during runtime at the cost of possible runtime errors 66 | * Trait `ExtiPin` allows connecting a pin to EXTI lines for interrupt and event handling 67 | * Struct `Debugger` marks pins that upon bootup are dedicated to the DBG peripheral, must be `activate` before use as a gpio 68 | * Trait `PinExt` adds functions to retrieve pin number and port id ('A', 'B', etc) 69 | - gpio Pins can be temporarily mode changed to do an action, using these methods: 70 | * `as_push_pull_output` 71 | * `as_push_pull_output_with_state` 72 | * `as_open_drain_output` 73 | * `as_open_drain_output_with_state` 74 | * `as_floating_input` 75 | * `as_pull_up_input` 76 | * `as_pull_down_input` 77 | - module `rcc` 78 | * new traits and implementations `Enable`, `Reset`, `BusClock`, `BusTimerClock`, `RccBus` 79 | * added function `debug_stop_mode` for control of clock function in sleep and stop conditions 80 | - added module `rtc` and examples thereof 81 | - module `serial` 82 | * Implemented embedded-hal v1.0 nb::Read/Write traits 83 | * Implemented embedded-hal v1.0 io::Write trait 84 | * Implemented `with_dma` methods, so Rx/Tx can use DMA 85 | * Added `SerialExt` trait and implementations for construction methods 86 | * Can configure stop bits, parity, word length 87 | * added `reconfigure` 88 | - module `spi` 89 | * Implemented embedded-hal v1.0 SpiBus, nb, and blocking, traits 90 | * Implemented `with_dma` methods, both Rx/Tx can use DMA, separately and together 91 | * Added `SpiExt` trait and implementations for construction methods 92 | * Works with both 8 and 16 bit words, though 16bit not tested 93 | * Added `SpiSlave` for slave functionality, though it is not tested 94 | * Added frame size conversion methods, [ex: `frame_size_8_bit`] 95 | - module `timer` 96 | * Old module `timers` was removed. Now follows both hal 1.0 and 0.2.7 traits 97 | 98 | ### Changed 99 | 100 | - Fixed repo url's 101 | - Changed all examples to use new api's where necessary, and to remove code structure that was causing much of the example to be optimized away 102 | - module `adc` changed to use new rcc enable, reset, and bus frequencies 103 | - module `gpio` 104 | * pin `into_` fns have removed the atomic context parameter which is not needed depending on what OS is used 105 | * implemented `split_without_reset` 106 | * Pin type doesn't represent an erased pin, but the most specific type of pin, see Added above 107 | * embedded-hal v0.2.7 traits moved to gpio/hal_02 module 108 | - module `i2c` changed to use rcc enable, reset, and bus frequencies 109 | - module `prelude` removed all embedded-hal traits 110 | - module `pwm` changed to use rcc enable, reset, and bus frequencies 111 | - module `serial` 112 | * embedded-hal v0.2.7 trait implementations moved to serial/hal_02 module 113 | * changed to use rcc enable, reset, and bus frequencies 114 | - module `spi` 115 | * embedded-hal v0.2.7 trait implementations moved to spi/hal_02 module 116 | * changed to use rcc enable, reset, and bus frequencies 117 | - module `time` changed to use fugit crate, this alters how you specify frequency, ie, hz -> Hz, etc 118 | 119 | ### Removed 120 | 121 | - `delay` and `timers` modules removed, the functionality is in the `timer` module now 122 | - `spi::Event::Crc` removed, as that doesn't exist on this micro 123 | - `timers` module removed, replaced by `timer` 124 | 125 | ### Fixed 126 | 127 | - Fixed github action `changelog.yml` 128 | - Fixed github action `ci.yml` 129 | - Fixed `tool/check.py` 130 | 131 | ## v0.1.1 - 2024-10-10 132 | 133 | ## V0.1.0 - 2024-09-07 134 | 135 | ## v0.0.1 - 2023-06-10 136 | 137 | - Original Release 138 | 139 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 140 | and this project adheres to [Semantic Versioning](http://semver.org/). 141 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "py32f0xx-hal" 3 | version = "0.3.1" 4 | authors = [ 5 | "creatoy@yeah.net", 6 | "Greg Green ", 7 | ] 8 | edition = "2021" 9 | 10 | keywords = ["arm", "cortex-m", "py32f0xx", "hal"] 11 | license = "MIT OR Apache-2.0" 12 | readme = "README.md" 13 | repository = "https://github.com/py32-rust/py32f0xx-hal" 14 | categories = ["embedded", "hardware-support", "no-std"] 15 | description = "Peripheral access API for py32F0 series microcontrollers" 16 | documentation = "https://docs.rs/crate/py32f0xx-hal" 17 | 18 | [package.metadata.docs.rs] 19 | features = ["py32f030", "rt", "rtic"] 20 | targets = ["thumbv6m-none-eabi"] 21 | 22 | [dependencies] 23 | bare-metal = { version = "1.0.0" } 24 | cast = "0.3.0" 25 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 26 | py32f0 = "0.2.1" 27 | embedded-hal = "1.0" 28 | embedded-hal-nb = "1.0" 29 | embedded-dma = "0.2.0" 30 | embedded-io = "0.6.1" 31 | nb = "1.1.0" 32 | void = { version = "1.0.2", default-features = false } 33 | defmt = { version = "0.3.8", optional = true } 34 | fugit = "0.3.7" 35 | fugit-timer = "0.1.3" 36 | rtic-monotonic = { version = "1.0", optional = true } 37 | 38 | [dependencies.embedded-hal-02] 39 | package = "embedded-hal" 40 | version = "0.2.7" 41 | features = ["unproven"] 42 | 43 | [dev-dependencies] 44 | cortex-m-rt = "0.7.3" 45 | panic-halt = "0.2.0" 46 | panic-probe = { version = "0.3.2", features = ["print-defmt"] } 47 | defmt = "0.3.8" 48 | defmt-rtt = "0.4.1" 49 | mfrc522 = "0.6.1" 50 | 51 | [features] 52 | device-selected = [] 53 | rt = ["py32f0/rt"] 54 | py32f030 = ["py32f0/py32f030", "device-selected", "with-dma"] 55 | py32f003 = ["py32f0/py32f003", "device-selected", "with-dma"] 56 | py32f002a = ["py32f0/py32f002a", "device-selected"] 57 | py32f002b = ["py32f0/py32f002b", "device-selected"] 58 | 59 | defmt = ["dep:defmt"] 60 | 61 | # Features based on Flash size (in kbytes) 62 | flash-16 = [] 63 | flash-20 = [] 64 | flash-24 = [] 65 | flash-32 = [] 66 | flash-48 = [] 67 | flash-64 = [] 68 | 69 | # Features based on RAM size (in kbytes) 70 | ram-2 = [] 71 | ram-3 = [] 72 | ram-4 = [] 73 | ram-6 = [] 74 | ram-8 = [] 75 | 76 | # optional peripherals 77 | with-dma = [] 78 | 79 | # MCU aliases 80 | # 81 | # Features correspond specific mcu series 82 | # Note: The easiest way to pick the proper package feature is to apply 83 | # the matching feature for your MCU! 84 | py32f030xx4 = ["py32f030", "flash-16", "ram-2"] 85 | py32f030xx6 = ["py32f030", "flash-32", "ram-4"] 86 | py32f030xx7 = ["py32f030", "flash-48", "ram-6"] 87 | py32f030xx8 = ["py32f030", "flash-64", "ram-8"] 88 | 89 | py32f003xx4 = ["py32f003", "flash-16", "ram-2"] 90 | py32f003xx6 = ["py32f003", "flash-32", "ram-4"] 91 | py32f003xx8 = ["py32f003", "flash-64", "ram-8"] 92 | 93 | py32f002ax5 = ["py32f002a", "flash-20", "ram-3"] 94 | py32f002bx5 = ["py32f002b", "flash-24", "ram-3"] 95 | 96 | # rtic os 97 | rtic = ["dep:rtic-monotonic"] 98 | 99 | [profile.dev] 100 | debug = true 101 | lto = true 102 | 103 | [profile.release] 104 | lto = true 105 | debug = true 106 | opt-level = "s" 107 | -------------------------------------------------------------------------------- /Embed.toml: -------------------------------------------------------------------------------- 1 | [default.probe] 2 | # USB vendor ID 3 | # usb_vid = "0d28" 4 | # USB product ID 5 | # usb_pid = "0204" 6 | # Serial number 7 | # serial = "12345678" 8 | # The protocol to be used for communicating with the target. 9 | protocol = "Swd" 10 | # The speed in kHz of the data link to the target. 11 | speed = 10000 12 | 13 | [default.flashing] 14 | # Whether or not the target should be flashed. 15 | enabled = true 16 | # Whether or not bytes erased but not rewritten with data from the ELF 17 | # should be restored with their contents before erasing. 18 | restore_unwritten_bytes = false 19 | # The path where an SVG of the assembled flash layout should be written to. 20 | # flash_layout_output_path = "out.svg" 21 | # Triggers a full chip erase instead of a page by page erase. 22 | do_chip_erase = false 23 | 24 | [default.reset] 25 | # Whether or not the target should be reset. 26 | # When flashing is enabled as well, the target will be reset after flashing. 27 | enabled = true 28 | # Whether or not the target should be halted after reset. 29 | halt_afterwards = false 30 | 31 | [default.general] 32 | # The chip name of the chip to be debugged. 33 | chip = "PY32F030x6" 34 | # A list of chip descriptions to be loaded during runtime. 35 | chip_descriptions = ["PY32F0_Series.yaml"] 36 | # The default log level to be used. Possible values are one of: 37 | # "OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE" 38 | log_level = "INFO" 39 | # Use this flag to assert the nreset & ntrst pins during attaching the probe to the chip. 40 | connect_under_reset = false 41 | 42 | [default.rtt] 43 | # Whether or not an RTTUI should be opened after flashing. 44 | enabled = true 45 | # How the target handles RTT outputs that won't fit in the buffer. This can be 46 | # overridden per-channel. If left unset, the firmware will determine the default 47 | # for each RTT up channel. 48 | # NoBlockSkip - Skip writing the data completely if it doesn't fit in its 49 | # entirety. 50 | # NoBlockTrim - Write as much as possible of the data and ignore the rest. 51 | # BlockIfFull - Spin until the host reads data. Can result in app freezing. 52 | # 53 | # up_mode = "BlockIfFull" 54 | 55 | # A list of channel associations to be displayed. If left empty, all channels are displayed. 56 | # up, down (Optional) - RTT channel numbers 57 | # name (Optional) - String to be displayed in the RTTUI tab 58 | # up_mode (Optional) - RTT channel specific as described above 59 | # format (Required) - How to interpret data from target firmware. One of: 60 | # String - Directly show output from the target 61 | # Defmt - Format output on the host, see https://defmt.ferrous-systems.com/ 62 | # BinaryLE - Display as raw hex 63 | channels = [ 64 | { up = 0, down = 0, name = "RTT", up_mode = "NoBlockSkip", format = "String" }, 65 | ] 66 | # The duration in ms for which the logger should retry to attach to RTT. 67 | timeout = 3000 68 | # Whether timestamps in the RTTUI are enabled 69 | show_timestamps = true 70 | # Whether to save rtt history buffer on exit. 71 | log_enabled = false 72 | # Where to save rtt history buffer relative to manifest path. 73 | log_path = "./logs" 74 | 75 | [default.gdb] 76 | # Whether or not a GDB server should be opened after flashing. 77 | enabled = false 78 | # The connection string in host:port format wher the GDB server will open a socket. 79 | gdb_connection_string = "127.0.0.1:50001" 80 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 The py32f0xx-hal authors. 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | py32f0xx-hal 2 | ================ 3 | 4 | > [HAL] for the py32f0xx family of microcontrollers 5 | 6 | [![Crates.io](https://img.shields.io/crates/d/py32f0xx-hal.svg)](https://crates.io/crates/py32f0xx-hal) 7 | [![Crates.io](https://img.shields.io/crates/v/py32f0xx-hal.svg)](https://crates.io/crates/py32f0xx-hal) 8 | [![docs.rs](https://docs.rs/py32f0xx-hal/badge.svg)](https://docs.rs/py32f0xx-hal/) 9 | [![dependency status](https://deps.rs/repo/github/[py32-rust/py32f0xx-hal/status.svg)](https://deps.rs/repo/github/py32-rust/py32f0xx-hal) 10 | [![Continuous integration](https://github.com/creatoy/py32f0xx-hal/workflows/Continuous%20integration/badge.svg)](https://github.com/py32-rust/py32f0xx-hal) 11 | 12 | [_py32f0xx-hal_](https://github.com/py32-rust/py32f0xx-hal) contains a hardware abstraction on top of the peripheral access API for the puyasemi PY32F0xx family of microcontrollers. 13 | 14 | Collaboration on this crate is highly welcome, as are pull requests! 15 | 16 | Supported 17 | ------------------------ 18 | 19 | * __py32f030__ (py32f030xx4, py32f030xx6, py32f030xx7, py32f030xx8) 20 | * __py32f003__ (py32f003xx4, py32f003xx6, py32f030xx8) 21 | * __py32f002a__ (py32f002ax5) 22 | * __py32f002b__ (py32f002bx5) 23 | 24 | | Family | F002A | F002A | F030/F003 | 25 | | ---------- | ---------- | --------------- |--------------- | 26 | | RCC | ✅ | ✅ | ✅ | 27 | | GPIO | ✅ | ✅ | ✅ | 28 | | INTERRUPT | ✅ | ✅ | ✅ | 29 | | DMA | N/A | N/A | ✅ | 30 | | EXTI | ✅ | ✅ | ✅ | 31 | | USART | ✅ | ✅ | ✅ | 32 | | I2C | ❓ | ❓ | ❓ | 33 | | SPI | ✅ | ✅ | ✅ | 34 | | ADC | ✅ | ✅ | ✅ | 35 | | RTC | ✅ | ✅ | ✅ | 36 | | FLASH | | | | 37 | | COMP | | | | 38 | | Timer(PWM) | ✅ | ✅ | ✅ | 39 | | Watchdog | ❓ | ❓ | ❓ | 40 | | LED | N/A | N/A | | 41 | 42 | 43 | - ✅ : Implemented 44 | - Blank : Not implemented 45 | - ❓ : Requires demo verification 46 | - `+` : Async support 47 | - N/A : Not available 48 | 49 | ## TODOs 50 | 51 | - LSE/LSI test and examples 52 | 53 | - Other chips 54 | 55 | Getting Started 56 | --------------- 57 | The `examples` folder contains several example programs. To compile them, one must specify the target device as cargo feature: 58 | ``` 59 | $ cargo build --features=py32f030 --example=blinky 60 | ``` 61 | 62 | To use py32f0xx-hal as a dependency in a standalone project the target device feature must be specified in the `Cargo.toml` file: 63 | ``` 64 | [dependencies] 65 | embedded-hal = "1" 66 | nb = "1" 67 | cortex-m = "0.7.7" 68 | cortex-m-rt = "0.7.3" 69 | # Panic behaviour, see https://crates.io/keywords/panic-impl for alternatives 70 | panic-halt = "0.2.0" 71 | py32f0xx-hal = { version = "0.3.1", features = ["py32f030"] } 72 | ``` 73 | 74 | ## Optional Features 75 | 76 | - __rtic__ this feature includes a `monotonic` timer module for use with that crate 77 | - __defmt__ Adds ```derive(defmt::Format)``` to `Error` types in this crate 78 | - __rt__ Enables the `rt` feature in the `py32f0` crate 79 | 80 | If you are unfamiliar with embedded development using Rust, there are a number of fantastic resources available to help. 81 | 82 | - [Embedded Rust Documentation](https://docs.rust-embedded.org/) 83 | - [The Embedded Rust Book](https://docs.rust-embedded.org/book/) 84 | - [Rust Embedded FAQ](https://docs.rust-embedded.org/faq.html) 85 | - [rust-embedded/awesome-embedded-rust](https://github.com/rust-embedded/awesome-embedded-rust) 86 | 87 | 88 | Minimum supported Rust version 89 | ------------------------------ 90 | 91 | The minimum supported Rust version is the latest stable release. Older versions may compile, especially when some features are not used in your application. 92 | 93 | Changelog 94 | --------- 95 | 96 | See [CHANGELOG.md](CHANGELOG.md). 97 | 98 | 99 | Credits 100 | ------- 101 | 102 | > This repo was inspired by [stm32f0xx-hal](https://github.com/stm32-rs/stm32f0xx-hal) and [stm32f1xx-hal](https://github.com/stm32-rs/stm32f1xx-hal) 103 | 104 | License 105 | ------- 106 | 107 | Licensed under either of 108 | 109 | * Apache License, Version 2.0 110 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 111 | * MIT license 112 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 113 | 114 | at your option. 115 | 116 | ## Contribution 117 | 118 | Unless you explicitly state otherwise, any contribution intentionally submitted 119 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 120 | dual licensed as above, without any additional terms or conditions. 121 | -------------------------------------------------------------------------------- /examples/adc_values.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use core::{cell::RefCell, fmt::Write}; 5 | 6 | use defmt_rtt as _; 7 | use panic_halt as _; 8 | 9 | use py32f0xx_hal as hal; 10 | 11 | use crate::hal::{gpio, pac, prelude::*}; 12 | 13 | use cortex_m::{interrupt::Mutex, peripheral::syst::SystClkSource::Core}; 14 | use cortex_m_rt::{entry, exception}; 15 | use defmt::info; 16 | 17 | struct Shared { 18 | adc: hal::adc::Adc, 19 | tx: hal::serial::Tx, 20 | ain0: gpio::gpioa::PA0, 21 | ain1: gpio::gpioa::PA1, 22 | } 23 | 24 | static SHARED: Mutex>> = Mutex::new(RefCell::new(None)); 25 | 26 | #[entry] 27 | fn main() -> ! { 28 | let dp = hal::pac::Peripherals::take().unwrap(); 29 | let cp = cortex_m::peripheral::Peripherals::take().unwrap(); 30 | cortex_m::interrupt::free(move |cs| { 31 | let mut flash = dp.FLASH; 32 | let rcc = dp.RCC.configure().sysclk(24.MHz()).freeze(&mut flash); 33 | 34 | let gpioa = dp.GPIOA.split(); 35 | 36 | let mut syst = cp.SYST; 37 | 38 | // Set source for SysTick counter, here full operating frequency (== 24MHz) 39 | syst.set_clock_source(Core); 40 | 41 | // Set reload value, i.e. timer delay 24 MHz/counts 42 | syst.set_reload(24_000_000 - 1); 43 | 44 | // Start SysTick counter 45 | syst.enable_counter(); 46 | 47 | // Start SysTick interrupt generation 48 | syst.enable_interrupt(); 49 | 50 | // USART1 at PA2 (TX) and PA3(RX) 51 | let tx = gpioa.pa2.into_alternate_af1(); 52 | let _rx = gpioa.pa3.into_alternate_af1(); // don't need, can be removed 53 | 54 | // Initialise UART for transmission only 55 | let mut tx = dp.USART1.tx(tx, 115_200.bps(), &rcc.clocks); 56 | 57 | // Initialise ADC 58 | let adc = hal::adc::Adc::new(dp.ADC, hal::adc::AdcClockMode::default()); 59 | 60 | let ain0 = gpioa.pa0.into_analog(); // ADC_IN0 61 | let ain1 = gpioa.pa1.into_analog(); // ADC_IN1 62 | 63 | // Output a friendly greeting 64 | tx.write_str("\n\rThis ADC example will read various values using the ADC and print them out to the serial terminal\r\n").ok(); 65 | info!("\n\rThis ADC example will read various values using the ADC and print them out to the serial terminal\r\n"); 66 | 67 | // Move all components under Mutex supervision 68 | *SHARED.borrow(cs).borrow_mut() = Some(Shared { 69 | adc, 70 | tx, 71 | ain0, 72 | ain1, 73 | }); 74 | }); 75 | loop {} 76 | } 77 | 78 | #[exception] 79 | fn SysTick() { 80 | use core::ops::DerefMut; 81 | 82 | // Enter critical section 83 | cortex_m::interrupt::free(|cs| { 84 | // Get access to the Mutex protected shared data 85 | if let Some(ref mut shared) = SHARED.borrow(cs).borrow_mut().deref_mut() { 86 | // Read temperature data from internal sensor using ADC 87 | let t = hal::adc::VTemp::read(&mut shared.adc, None); 88 | writeln!(shared.tx, "Temperature {}.{}C\r", t / 100, t % 100).ok(); 89 | info!("Temperature {}.{}C\r", t / 100, t % 100); 90 | 91 | // Read voltage reference data from internal sensor using ADC 92 | let t = hal::adc::VRef::read_vdda(&mut shared.adc); 93 | writeln!(shared.tx, "Vdda {}mV\r", t).ok(); 94 | info!("Vdda {}mV\r", t); 95 | 96 | // Read voltage data from external pin using ADC 97 | let v0 = hal::adc::Adc::read_abs_mv(&mut shared.adc, &mut shared.ain0); 98 | let v1 = hal::adc::Adc::read_abs_mv(&mut shared.adc, &mut shared.ain1); 99 | writeln!(shared.tx, "Ain0 {}mV Ain1 {}mV\r", v0, v1).ok(); 100 | info!("Ain0 {}mV Ain1 {}mV\r", v0, v1); 101 | } 102 | }); 103 | } 104 | -------------------------------------------------------------------------------- /examples/blinky.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use py32f0xx_hal as hal; 7 | 8 | use crate::hal::{pac, prelude::*}; 9 | 10 | use cortex_m_rt::entry; 11 | 12 | #[entry] 13 | fn main() -> ! { 14 | let p = pac::Peripherals::take().unwrap(); 15 | 16 | let gpioa = p.GPIOA.split(); 17 | 18 | // (Re-)configure PA5 as output 19 | let mut led = gpioa.pa5.into_push_pull_output(); 20 | 21 | loop { 22 | // Turn PA5 on a million times in a row 23 | for _ in 0..1_000_000 { 24 | led.set_high(); 25 | } 26 | // Then turn PA5 off a million times in a row 27 | for _ in 0..1_000_000 { 28 | led.set_low(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/blinky_adc.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use py32f0xx_hal as hal; 7 | 8 | use crate::hal::{adc::Adc, pac, prelude::*}; 9 | 10 | use cortex_m::peripheral::Peripherals; 11 | use cortex_m_rt::entry; 12 | use embedded_hal_02::adc::OneShot; 13 | use embedded_hal_02::blocking::delay::DelayMs; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | let mut p = pac::Peripherals::take().unwrap(); 18 | let cp = Peripherals::take().unwrap(); 19 | let rcc = p.RCC.configure().freeze(&mut p.FLASH); 20 | 21 | let gpioa = p.GPIOA.split(); 22 | 23 | // (Re-)configure PA5 as output 24 | let mut led = gpioa.pa5.into_push_pull_output(); 25 | // (Re-)configure PA0 as analog input 26 | let mut an_in = gpioa.pa0.into_analog(); 27 | 28 | // Get delay provider 29 | let mut delay = cp.SYST.delay(&rcc.clocks); 30 | 31 | // Get access to the ADC 32 | let mut adc = Adc::new(p.ADC, hal::adc::AdcClockMode::Pclk); 33 | 34 | loop { 35 | led.toggle(); 36 | 37 | let time: u16 = if let Ok(val) = adc.read(&mut an_in) as Result { 38 | /* shift the value right by 3, same as divide by 8, reduces 39 | the 0-4095 range into something approximating 1-512 */ 40 | (val >> 3) + 1 41 | } else { 42 | 1000 43 | }; 44 | 45 | delay.delay_ms(time); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/blinky_delay.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use py32f0xx_hal as hal; 7 | 8 | use crate::hal::{pac, prelude::*}; 9 | use cortex_m::peripheral::Peripherals; 10 | use cortex_m_rt::entry; 11 | use embedded_hal_02::blocking::delay::DelayMs; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | let mut p = pac::Peripherals::take().unwrap(); 16 | let cp = Peripherals::take().unwrap(); 17 | let rcc = p.RCC.configure().freeze(&mut p.FLASH); 18 | 19 | let gpioa = p.GPIOA.split(); 20 | 21 | // (Re-)configure PA5 as output 22 | let mut led = gpioa.pa5.into_push_pull_output(); 23 | 24 | // Get delay provider 25 | let mut delay = cp.SYST.delay(&rcc.clocks); 26 | 27 | loop { 28 | led.toggle(); 29 | delay.delay_ms(1_000_u16); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/blinky_multiple.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use py32f0xx_hal as hal; 7 | 8 | use crate::hal::{pac, prelude::*}; 9 | 10 | use cortex_m::peripheral::Peripherals; 11 | use cortex_m_rt::entry; 12 | use embedded_hal_02::blocking::delay::DelayMs; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | let mut p = pac::Peripherals::take().unwrap(); 17 | let cp = Peripherals::take().unwrap(); 18 | let rcc = p.RCC.configure().freeze(&mut p.FLASH); 19 | 20 | let gpioa = p.GPIOA.split(); 21 | let gpiob = p.GPIOB.split(); 22 | 23 | // (Re-)configure PA5 as output 24 | let led1 = gpioa.pa5.into_push_pull_output(); 25 | // (Re-)configure PB1 as output 26 | let led2 = gpiob.pb1.into_push_pull_output(); 27 | 28 | // Get delay provider 29 | let mut delay = cp.SYST.delay(&rcc.clocks); 30 | 31 | // Store them together after erasing the type 32 | let mut leds = [led1.erase(), led2.erase()]; 33 | loop { 34 | for l in &mut leds { 35 | l.toggle(); 36 | } 37 | delay.delay_ms(1_000_u16); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/blinky_rtc.rs: -------------------------------------------------------------------------------- 1 | //! Blinks an LED 2 | //! 3 | //! This assumes that a LED is connected to pa12 4 | 5 | #![deny(unsafe_code)] 6 | #![no_std] 7 | #![no_main] 8 | 9 | use panic_halt as _; 10 | 11 | use py32f0xx_hal::{pac, prelude::*, rtc::Rtc}; 12 | 13 | use cortex_m_rt::entry; 14 | use nb::block; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | let dp = pac::Peripherals::take().unwrap(); 19 | 20 | // Set up the GPIO pin 21 | let gpioa = dp.GPIOA.split(); 22 | let mut led = gpioa.pa12.into_push_pull_output(); 23 | 24 | // Set up the RTC 25 | // Start the RTC 26 | let mut rtc = Rtc::new(dp.RTC); 27 | 28 | let mut led_on = false; 29 | loop { 30 | // Set the current time to 0 31 | rtc.set_time(0); 32 | // Trigger the alarm in 5 seconds 33 | rtc.set_alarm(5); 34 | block!(rtc.wait_alarm()).unwrap(); 35 | if led_on { 36 | led.set_low(); 37 | led_on = false; 38 | } else { 39 | led.set_high(); 40 | led_on = true; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/blinky_timer.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use py32f0xx_hal as hal; 7 | 8 | use crate::hal::{pac, prelude::*}; 9 | 10 | use cortex_m_rt::entry; 11 | 12 | #[entry] 13 | fn main() -> ! { 14 | let mut p = pac::Peripherals::take().unwrap(); 15 | let rcc = p.RCC.configure().freeze(&mut p.FLASH); 16 | 17 | let gpioa = p.GPIOA.split(); 18 | 19 | // (Re-)configure PA5 as output 20 | let mut led = gpioa.pa5.into_push_pull_output(); 21 | 22 | // Set up a timer expiring after 200ms 23 | let mut timer = p.TIM1.counter_hz(&rcc.clocks); 24 | timer.start(5.Hz()).unwrap(); 25 | 26 | loop { 27 | led.toggle(); 28 | 29 | // Wait for the timer to expire 30 | nb::block!(timer.wait()).ok(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/blinky_timer_irq.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use py32f0xx_hal as hal; 7 | 8 | use crate::hal::{ 9 | gpio::{gpioa, Output, PushPull}, 10 | pac::{interrupt, Interrupt, Peripherals, TIM16}, 11 | prelude::*, 12 | timer::*, 13 | }; 14 | 15 | use core::cell::RefCell; 16 | use cortex_m::{interrupt::Mutex, peripheral::Peripherals as c_m_Peripherals}; 17 | use cortex_m_rt::entry; 18 | 19 | // A type definition for the GPIO pin to be used for our LED 20 | type LEDPIN = gpioa::PA5>; 21 | 22 | // Make LED pin globally available 23 | static GLED: Mutex>> = Mutex::new(RefCell::new(None)); 24 | 25 | // Make timer interrupt registers globally available 26 | static GINT: Mutex>>> = Mutex::new(RefCell::new(None)); 27 | 28 | // Define an interupt handler, i.e. function to call when interrupt occurs. Here if our external 29 | // interrupt trips when the timer timed out 30 | #[interrupt] 31 | fn TIM16() { 32 | static mut LED: Option = None; 33 | static mut INT: Option> = None; 34 | 35 | let led = LED.get_or_insert_with(|| { 36 | cortex_m::interrupt::free(|cs| { 37 | // Move LED pin here, leaving a None in its place 38 | GLED.borrow(cs).replace(None).unwrap() 39 | }) 40 | }); 41 | 42 | let int = INT.get_or_insert_with(|| { 43 | cortex_m::interrupt::free(|cs| { 44 | // Move LED pin here, leaving a None in its place 45 | GINT.borrow(cs).replace(None).unwrap() 46 | }) 47 | }); 48 | 49 | led.toggle(); 50 | int.wait().ok(); 51 | } 52 | 53 | #[entry] 54 | fn main() -> ! { 55 | let mut p = Peripherals::take().unwrap(); 56 | let cp = c_m_Peripherals::take().unwrap(); 57 | cortex_m::interrupt::free(move |cs| { 58 | let rcc = p 59 | .RCC 60 | .configure() 61 | .sysclk(24.MHz()) 62 | .pclk(24.MHz()) 63 | .freeze(&mut p.FLASH); 64 | 65 | let gpioa = p.GPIOA.split(); 66 | 67 | // (Re-)configure PA5 as output 68 | let led = gpioa.pa5.into_push_pull_output(); 69 | 70 | // Move the pin into our global storage 71 | *GLED.borrow(cs).borrow_mut() = Some(led); 72 | 73 | // Set up a timer expiring after 1s 74 | let mut timer = p.TIM16.counter_hz(&rcc.clocks); 75 | 76 | // Generate an interrupt when the timer expires 77 | timer.listen(Event::Update); 78 | timer.start(1.Hz()).unwrap(); 79 | 80 | // Move the timer into our global storage 81 | *GINT.borrow(cs).borrow_mut() = Some(timer); 82 | 83 | // Enable TIM16 IRQ, set prio 1 and clear any pending IRQs 84 | let mut nvic = cp.NVIC; 85 | unsafe { 86 | nvic.set_priority(Interrupt::TIM16, 1); 87 | cortex_m::peripheral::NVIC::unmask(Interrupt::TIM16); 88 | } 89 | cortex_m::peripheral::NVIC::unpend(Interrupt::TIM16); 90 | }); 91 | loop {} 92 | } 93 | -------------------------------------------------------------------------------- /examples/exti.rs: -------------------------------------------------------------------------------- 1 | //! Turns the user LED on 2 | //! 3 | //! Listens for interrupts on the pa11 pin. On any rising or falling edge, toggles 4 | //! the pa12 pin (which is connected to the LED on the dev board, hence the `led` name). 5 | 6 | #![allow(clippy::empty_loop)] 7 | #![no_main] 8 | #![no_std] 9 | 10 | use panic_halt as _; 11 | 12 | use core::mem::MaybeUninit; 13 | use cortex_m_rt::entry; 14 | use pac::interrupt; 15 | use py32f0xx_hal::gpio::*; 16 | use py32f0xx_hal::{pac, prelude::*}; 17 | 18 | // These two are owned by the ISR. main() may only access them during the initialization phase, 19 | // where the interrupt is not yet enabled (i.e. no concurrent accesses can occur). 20 | // After enabling the interrupt, main() may not have any references to these objects any more. 21 | // For the sake of minimalism, we do not use RTIC here, which would be the better way. 22 | static mut LED: MaybeUninit>> = 23 | MaybeUninit::uninit(); 24 | static mut INT_PIN: MaybeUninit>> = 25 | MaybeUninit::uninit(); 26 | 27 | #[interrupt] 28 | #[allow(static_mut_refs)] 29 | fn EXTI4_15() { 30 | let led = unsafe { &mut *LED.as_mut_ptr() }; 31 | let int_pin = unsafe { &mut *INT_PIN.as_mut_ptr() }; 32 | 33 | if int_pin.check_interrupt() { 34 | led.toggle(); 35 | 36 | // if we don't clear this bit, the ISR would trigger indefinitely 37 | int_pin.clear_interrupt_pending_bit(); 38 | } 39 | } 40 | 41 | #[entry] 42 | fn main() -> ! { 43 | // initialization phase 44 | let mut p = pac::Peripherals::take().unwrap(); 45 | let _cp = cortex_m::peripheral::Peripherals::take().unwrap(); 46 | let _rcc = p.RCC.configure().freeze(&mut p.FLASH); 47 | { 48 | // the scope ensures that the int_pin reference is dropped before the first ISR can be executed. 49 | 50 | let gpioa = p.GPIOA.split(); 51 | 52 | #[allow(static_mut_refs)] 53 | let led = unsafe { &mut *LED.as_mut_ptr() }; 54 | *led = gpioa.pa12.into_push_pull_output(); 55 | 56 | #[allow(static_mut_refs)] 57 | let int_pin = unsafe { &mut *INT_PIN.as_mut_ptr() }; 58 | *int_pin = gpioa.pa11.into_floating_input(); 59 | int_pin.make_interrupt_source(&mut p.EXTI); 60 | int_pin.trigger_on_edge(&mut p.EXTI, Edge::RisingFalling); 61 | int_pin.enable_interrupt(&mut p.EXTI); 62 | } // initialization ends here 63 | 64 | unsafe { 65 | pac::NVIC::unmask(pac::Interrupt::EXTI4_15); 66 | } 67 | cortex_m::peripheral::NVIC::unpend(pac::Interrupt::EXTI4_15); 68 | 69 | loop {} 70 | } 71 | -------------------------------------------------------------------------------- /examples/flash_systick.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use defmt_rtt as _; 5 | use panic_halt as _; 6 | 7 | use py32f0xx_hal as hal; 8 | 9 | use crate::hal::{gpio::*, pac, prelude::*}; 10 | 11 | use cortex_m::{interrupt::Mutex, peripheral::syst::SystClkSource::Core, Peripherals}; 12 | use cortex_m_rt::{entry, exception}; 13 | 14 | use core::cell::RefCell; 15 | use core::ops::DerefMut; 16 | 17 | use defmt::info; 18 | 19 | // Mutex protected structure for our shared GPIO pin 20 | static GPIO: Mutex>>>> = Mutex::new(RefCell::new(None)); 21 | 22 | #[entry] 23 | fn main() -> ! { 24 | let mut p = pac::Peripherals::take().unwrap(); 25 | let cp = Peripherals::take().unwrap(); 26 | let _rcc = p.RCC.configure().sysclk(24.MHz()).freeze(&mut p.FLASH); 27 | 28 | let gpioa = p.GPIOA.split(); 29 | 30 | // (Re-)configure PA5 as output 31 | let led = gpioa.pa5.into_push_pull_output(); 32 | 33 | cortex_m::interrupt::free(move |cs| { 34 | // Transfer GPIO into a shared structure 35 | *GPIO.borrow(cs).borrow_mut() = Some(led); 36 | }); 37 | 38 | let mut syst = cp.SYST; 39 | 40 | // Initialise SysTick counter with a defined value 41 | unsafe { syst.cvr.write(1) }; 42 | 43 | // Set source for SysTick counter, here full operating frequency (== 48MHz) 44 | syst.set_clock_source(Core); 45 | 46 | // Set reload value, i.e. timer delay 24 MHz/2 Mcounts == 12Hz or 83ms 47 | syst.set_reload(2_000_000 - 1); 48 | 49 | // Start counting 50 | syst.enable_counter(); 51 | 52 | // Enable interrupt generation 53 | syst.enable_interrupt(); 54 | 55 | loop {} 56 | } 57 | 58 | // Define an exception handler, i.e. function to call when exception occurs. Here, if our SysTick 59 | // timer generates an exception the following handler will be called 60 | #[exception] 61 | fn SysTick() { 62 | // Exception handler state variable 63 | static mut STATE: u8 = 1; 64 | 65 | // Enter critical section 66 | cortex_m::interrupt::free(|cs| { 67 | // Borrow access to our GPIO pin from the shared structure 68 | if let Some(ref mut led) = *GPIO.borrow(cs).borrow_mut().deref_mut() { 69 | // Check state variable, keep LED off most of the time and turn it on every 10th tick 70 | if *STATE < 10 { 71 | // Turn off the LED 72 | led.set_low(); 73 | 74 | // And now increment state variable 75 | *STATE += 1; 76 | } else { 77 | // Turn on the LED 78 | led.set_high(); 79 | 80 | // And set new state variable back to 0 81 | *STATE = 0; 82 | } 83 | 84 | info!("Tick {}", *STATE); 85 | } 86 | }); 87 | } 88 | -------------------------------------------------------------------------------- /examples/flash_systick_fancier.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use py32f0xx_hal as hal; 7 | 8 | use crate::hal::{gpio::*, pac, prelude::*}; 9 | 10 | use cortex_m::{interrupt::Mutex, peripheral::syst::SystClkSource::Core, Peripherals}; 11 | use cortex_m_rt::{entry, exception}; 12 | 13 | use core::cell::RefCell; 14 | use core::mem::swap; 15 | 16 | // A type definition for the GPIO pin to be used for our LED 17 | type LEDPIN = gpioa::PA5>; 18 | 19 | // Mutex protected structure for our shared GPIO pin 20 | static GPIO: Mutex>> = Mutex::new(RefCell::new(None)); 21 | 22 | #[entry] 23 | fn main() -> ! { 24 | let mut p = pac::Peripherals::take().unwrap(); 25 | let cp = Peripherals::take().unwrap(); 26 | let _rcc = p.RCC.configure().sysclk(24.MHz()).freeze(&mut p.FLASH); 27 | 28 | // Get access to individual pins in the GPIO port 29 | let gpioa = p.GPIOA.split(); 30 | 31 | // (Re-)configure the pin connected to our LED as output 32 | let led = gpioa.pa5.into_push_pull_output(); 33 | 34 | cortex_m::interrupt::free(move |cs| { 35 | // Transfer GPIO into a shared structure 36 | *GPIO.borrow(cs).borrow_mut() = Some(led); 37 | }); 38 | 39 | let mut syst = cp.SYST; 40 | 41 | // Initialise SysTick counter with a defined value 42 | unsafe { syst.cvr.write(1) }; 43 | 44 | // Set source for SysTick counter, here full operating frequency (== 48MHz) 45 | syst.set_clock_source(Core); 46 | 47 | // Set reload value, i.e. timer delay 24 MHz/2 Mcounts == 12Hz or 83ms 48 | syst.set_reload(2_000_000 - 1); 49 | 50 | // Start counting 51 | syst.enable_counter(); 52 | 53 | // Enable interrupt generation 54 | syst.enable_interrupt(); 55 | 56 | loop {} 57 | } 58 | 59 | // Define an exception handler, i.e. function to call when exception occurs. Here, if our SysTick 60 | // timer generates an exception the following handler will be called 61 | #[exception] 62 | fn SysTick() { 63 | // Our moved LED pin 64 | static mut LED: Option = None; 65 | 66 | // Exception handler state variable 67 | static mut STATE: u8 = 1; 68 | 69 | // If LED pin was moved into the exception handler, just use it 70 | if let Some(led) = LED { 71 | // Check state variable, keep LED off most of the time and turn it on every 10th tick 72 | if *STATE < 10 { 73 | // Turn off the LED 74 | led.set_low(); 75 | 76 | // And now increment state variable 77 | *STATE += 1; 78 | } else { 79 | // Turn on the LED 80 | led.set_high(); 81 | 82 | // And set new state variable back to 0 83 | *STATE = 0; 84 | } 85 | } 86 | // Otherwise move it out of the Mutex protected shared region into our exception handler 87 | else { 88 | // Enter critical section 89 | cortex_m::interrupt::free(|cs| { 90 | // Swap globally stored data with SysTick private data 91 | swap(LED, &mut GPIO.borrow(cs).borrow_mut()); 92 | }); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /examples/i2c_find_address.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use defmt_rtt as _; 7 | 8 | use py32f0xx_hal as hal; 9 | 10 | use crate::hal::{i2c::I2c, pac, prelude::*}; 11 | 12 | use cortex_m_rt::entry; 13 | use defmt::info; 14 | use embedded_hal_02::blocking::i2c::Write; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | let p = pac::Peripherals::take().unwrap(); 19 | let mut flash = p.FLASH; 20 | let rcc = p 21 | .RCC 22 | .configure() 23 | .sysclk(24.MHz()) 24 | .pclk(24.MHz()) 25 | .freeze(&mut flash); 26 | 27 | let gpioa = p.GPIOA.split(); 28 | 29 | // Configure pins for I2C 30 | let scl = gpioa.pa3.into_alternate_af12(); 31 | let sda = gpioa.pa2.into_alternate_af12(); 32 | 33 | // Configure I2C with 100kHz rate 34 | let mut i2c = I2c::i2c(p.I2C, (scl, sda), 100.kHz(), &rcc.clocks); 35 | 36 | let mut devices = 0; 37 | // I2C addresses are 7-bit wide, covering the 0-127 range 38 | for add in 0..=127 { 39 | // The write method sends the specified address and checks for acknowledgement; 40 | // if no ack is given by the slave device the result is Err(), otherwise Ok() 41 | // Since we only care for an acknowledgement the data sent can be empty 42 | if i2c.write(add, &[]).is_ok() { 43 | devices += 1; 44 | } 45 | } 46 | 47 | // Here the variable "_devices" counts how many i2c addresses were a hit 48 | info!("{} devices find.\r\n", devices); 49 | 50 | loop { 51 | continue; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/led_hal_button_irq.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use py32f0xx_hal as hal; 7 | 8 | use crate::hal::{ 9 | gpio::*, 10 | pac::{interrupt, Interrupt, Peripherals}, 11 | prelude::*, 12 | timer::SysDelay, 13 | }; 14 | 15 | use cortex_m::{interrupt::Mutex, peripheral::Peripherals as c_m_Peripherals}; 16 | use cortex_m_rt::entry; 17 | 18 | use core::{cell::RefCell, ops::DerefMut}; 19 | use embedded_hal_02::blocking::delay::DelayMs; 20 | 21 | // Make our LED globally available 22 | static LED: Mutex>>>> = Mutex::new(RefCell::new(None)); 23 | 24 | // Make our delay provider globally available 25 | static DELAY: Mutex>> = Mutex::new(RefCell::new(None)); 26 | 27 | // Make button pin globally available 28 | static INT: Mutex>>>> = Mutex::new(RefCell::new(None)); 29 | 30 | #[entry] 31 | fn main() -> ! { 32 | let mut p = Peripherals::take().unwrap(); 33 | let cp = c_m_Peripherals::take().unwrap(); 34 | // Enable clock for SYSCFG 35 | let rcc = p.RCC; 36 | rcc.apbenr2.modify(|_, w| w.syscfgen().set_bit()); 37 | 38 | let mut flash = p.FLASH; 39 | let rcc = rcc.configure().sysclk(24.MHz()).freeze(&mut flash); 40 | 41 | let gpioa = p.GPIOA.split(); 42 | let gpiob = p.GPIOB.split(); 43 | 44 | // Configure PB2 as input (button) 45 | let mut int_pin = gpiob.pb2.into_pull_down_input(); 46 | int_pin.make_interrupt_source(&mut p.EXTI); 47 | int_pin.trigger_on_edge(&mut p.EXTI, Edge::Rising); 48 | int_pin.enable_interrupt(&mut p.EXTI); 49 | 50 | // Configure PA5 as output (LED) 51 | let mut led = gpioa.pa5.into_push_pull_output(); 52 | 53 | // Turn off LED 54 | led.set_low(); 55 | 56 | // Initialise delay provider 57 | let delay = cp.SYST.delay(&rcc.clocks); 58 | 59 | // Move control over LED and DELAY and EXTI into global mutexes 60 | cortex_m::interrupt::free(move |cs| { 61 | *LED.borrow(cs).borrow_mut() = Some(led); 62 | *DELAY.borrow(cs).borrow_mut() = Some(delay); 63 | *INT.borrow(cs).borrow_mut() = Some(int_pin); 64 | }); 65 | 66 | // Enable EXTI IRQ, set prio 1 and clear any pending IRQs 67 | let mut nvic = cp.NVIC; 68 | unsafe { 69 | nvic.set_priority(Interrupt::EXTI2_3, 1); 70 | cortex_m::peripheral::NVIC::unmask(Interrupt::EXTI2_3); 71 | } 72 | cortex_m::peripheral::NVIC::unpend(Interrupt::EXTI2_3); 73 | 74 | loop { 75 | continue; 76 | } 77 | } 78 | 79 | // Define an interrupt handler, i.e. function to call when interrupt occurs. Here if our external 80 | // interrupt trips when the button is pressed and will light the LED for a second 81 | #[interrupt] 82 | fn EXTI2_3() { 83 | // Enter critical section 84 | cortex_m::interrupt::free(|cs| { 85 | // Obtain all Mutex protected resources 86 | if let (&mut Some(ref mut led), &mut Some(ref mut delay), &mut Some(ref mut int_pin)) = ( 87 | LED.borrow(cs).borrow_mut().deref_mut(), 88 | DELAY.borrow(cs).borrow_mut().deref_mut(), 89 | INT.borrow(cs).borrow_mut().deref_mut(), 90 | ) { 91 | if int_pin.check_interrupt() { 92 | // Turn on LED 93 | led.set_high(); 94 | 95 | // Wait a second 96 | delay.delay_ms(1_000_u16); 97 | 98 | // Turn off LED 99 | led.set_low(); 100 | 101 | // Clear event triggering the interrupt 102 | int_pin.clear_interrupt_pending_bit(); 103 | } 104 | } 105 | }); 106 | } 107 | -------------------------------------------------------------------------------- /examples/pwm.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![no_main] 3 | #![no_std] 4 | 5 | // Halt on panic 6 | use panic_halt as _; 7 | 8 | use cortex_m_rt::entry; 9 | 10 | use py32f0xx_hal as hal; 11 | 12 | use hal::{pac, prelude::*}; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | let mut dp = pac::Peripherals::take().unwrap(); 17 | // Set up the system clock. 18 | let rcc = dp.RCC.configure().sysclk(24.MHz()).freeze(&mut dp.FLASH); 19 | 20 | let gpioa = dp.GPIOA.split(); 21 | let channels = ( 22 | gpioa.pa8.into_alternate_af2(), // on TIM1_CH1 23 | gpioa.pa9.into_alternate_af2(), // on TIM1_CH2 24 | ); 25 | 26 | let pwm = dp.TIM1.pwm_hz(channels, 20.kHz(), &rcc.clocks); 27 | let (mut ch1, mut ch2) = pwm.split(); 28 | let max_duty = ch1.get_max_duty(); 29 | ch1.set_duty(max_duty / 4); 30 | ch1.enable(); 31 | ch2.set_duty(max_duty * 9 / 10); 32 | ch2.enable(); 33 | 34 | loop { 35 | cortex_m::asm::nop(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/pwm_complementary.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![no_main] 3 | #![no_std] 4 | 5 | // Halt on panic 6 | use panic_halt as _; 7 | 8 | use cortex_m; 9 | use cortex_m_rt::entry; 10 | 11 | use py32f0xx_hal as hal; 12 | 13 | use embedded_hal_02::blocking::delay::DelayMs; 14 | use hal::{pac, prelude::*}; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | let mut dp = pac::Peripherals::take().unwrap(); 19 | let cp = cortex_m::Peripherals::take().unwrap(); 20 | // Set up the system clock. 21 | let rcc = dp.RCC.configure().sysclk(24.MHz()).freeze(&mut dp.FLASH); 22 | 23 | let gpioa = dp.GPIOA.split(); 24 | let channels = ( 25 | gpioa.pa8.into_alternate_af2(), // on TIM1_CH1 26 | gpioa.pa7.into_alternate_af2(), // on TIM1_CH1N 27 | ); 28 | 29 | let pwm = dp.TIM1.pwm_hz(channels, 20.kHz(), &rcc.clocks); 30 | let (mut ch1, mut ch1n) = pwm.split(); 31 | let max_duty = ch1.get_max_duty(); 32 | ch1.set_duty(max_duty / 2); 33 | ch1.enable(); 34 | ch1n.enable(); 35 | 36 | // simple duty sweep 37 | let mut delay = cp.SYST.delay(&rcc.clocks); 38 | 39 | let steps = 100; 40 | 41 | loop { 42 | for i in 0..steps { 43 | ch1.set_duty(max_duty / steps * i); 44 | delay.delay_ms(30u16); 45 | } 46 | 47 | for i in (1..steps).rev() { 48 | ch1.set_duty(max_duty / steps * i); 49 | delay.delay_ms(30u16); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/serial_echo.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use core::fmt::Write; 5 | 6 | use panic_halt as _; 7 | 8 | use py32f0xx_hal as hal; 9 | 10 | use crate::hal::{ 11 | pac, 12 | prelude::*, 13 | rcc::{HSIFreq, MCODiv, MCOSrc}, 14 | }; 15 | 16 | use cortex_m_rt::entry; 17 | use embedded_hal_02::serial::{Read, Write as OtherWrite}; 18 | 19 | #[entry] 20 | fn main() -> ! { 21 | let p = pac::Peripherals::take().unwrap(); 22 | let mut flash = p.FLASH; 23 | let rcc = p 24 | .RCC 25 | .configure() 26 | .hsi(HSIFreq::Freq24mhz) 27 | .sysclk(24.MHz()) 28 | .freeze(&mut flash); 29 | rcc.configure_mco(MCOSrc::Sysclk, MCODiv::NotDivided); 30 | 31 | let gpioa = p.GPIOA.split(); 32 | 33 | let (tx, rx) = ( 34 | gpioa.pa2.into_alternate_af1(), 35 | gpioa.pa3.into_alternate_af1(), 36 | ); 37 | 38 | let mut serial = p.USART1.serial((tx, rx), 115_200.bps(), &rcc.clocks); 39 | serial.write_str("Input any key:\r\n").ok(); 40 | 41 | loop { 42 | // Wait for reception of a single byte 43 | let received: u8 = nb::block!(serial.read()).unwrap(); 44 | 45 | // Send back previously received byte and wait for completion 46 | nb::block!(serial.write(received)).ok(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/serial_spi_bridge.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use py32f0xx_hal as hal; 7 | 8 | use crate::hal::{ 9 | pac, 10 | prelude::*, 11 | spi::{Mode, Phase, Polarity}, 12 | }; 13 | 14 | use nb::block; 15 | 16 | use cortex_m_rt::entry; 17 | use embedded_hal::spi::SpiBus; 18 | 19 | /// A basic serial to spi example 20 | /// 21 | /// If you connect MOSI & MISO pins together, you'll see all characters 22 | /// that you typed in your serial terminal echoed back 23 | /// 24 | /// If you connect MISO to GND, you'll see nothing coming back 25 | #[entry] 26 | fn main() -> ! { 27 | const MODE: Mode = Mode { 28 | polarity: Polarity::IdleHigh, 29 | phase: Phase::CaptureOnSecondTransition, 30 | }; 31 | 32 | let p = pac::Peripherals::take().unwrap(); 33 | let mut flash = p.FLASH; 34 | let rcc = p.RCC.configure().freeze(&mut flash); 35 | 36 | let gpioa = p.GPIOA.split(); 37 | 38 | let (sck, miso, mosi, tx, rx) = ( 39 | // SPI pins 40 | gpioa.pa5.into_alternate_af0(), 41 | gpioa.pa6.into_alternate_af0(), 42 | gpioa.pa7.into_alternate_af0(), 43 | // USART pins 44 | gpioa.pa2.into_alternate_af1(), 45 | gpioa.pa3.into_alternate_af1(), 46 | ); 47 | 48 | // Configure SPI with 1MHz rate 49 | let mut spi = p.SPI1.spi( 50 | (Some(sck), Some(miso), Some(mosi)), 51 | MODE, 52 | 1.MHz(), 53 | &rcc.clocks, 54 | ); 55 | 56 | let mut serial = p.USART1.serial((tx, rx), 115_200.bps(), &rcc.clocks); 57 | 58 | let mut datatx = [0]; 59 | let datarx = [0]; 60 | 61 | loop { 62 | let serial_received = block!(serial.rx.read()).unwrap(); 63 | spi.write(&[serial_received]).ok(); 64 | spi.transfer(&mut datatx, &datarx).unwrap(); 65 | block!(serial.tx.write_u8(datarx[0])).ok(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/serial_stopwatch.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use py32f0xx_hal as hal; 7 | 8 | use crate::hal::{ 9 | pac::{interrupt, Interrupt, Peripherals, TIM16}, 10 | prelude::*, 11 | timer::{CounterMs, Event}, 12 | }; 13 | use core::cell::RefCell; 14 | use core::fmt::Write as _; 15 | use core::ops::DerefMut; 16 | use cortex_m::{interrupt::Mutex, peripheral::Peripherals as c_m_Peripherals}; 17 | use cortex_m_rt::entry; 18 | 19 | // Make timer interrupt registers globally available 20 | static GINT: Mutex>>> = Mutex::new(RefCell::new(None)); 21 | 22 | #[derive(Copy, Clone)] 23 | struct Time { 24 | seconds: u32, 25 | millis: u16, 26 | } 27 | 28 | static TIME: Mutex> = Mutex::new(RefCell::new(Time { 29 | seconds: 0, 30 | millis: 0, 31 | })); 32 | 33 | // Define an interupt handler, i.e. function to call when interrupt occurs. Here if our external 34 | // interrupt trips when the timer timed out 35 | #[interrupt] 36 | fn TIM16() { 37 | cortex_m::interrupt::free(|cs| { 38 | // Move LED pin here, leaving a None in its place 39 | GINT.borrow(cs) 40 | .borrow_mut() 41 | .deref_mut() 42 | .as_mut() 43 | .unwrap() 44 | .wait() 45 | .ok(); 46 | let mut time = TIME.borrow(cs).borrow_mut(); 47 | time.millis += 1; 48 | if time.millis == 1000 { 49 | time.millis = 0; 50 | time.seconds += 1; 51 | } 52 | }); 53 | } 54 | 55 | #[entry] 56 | fn main() -> ! { 57 | let p = Peripherals::take().unwrap(); 58 | let cp = c_m_Peripherals::take().unwrap(); 59 | 60 | let mut flash = p.FLASH; 61 | let rcc = p.RCC.configure().sysclk(24.MHz()).freeze(&mut flash); 62 | 63 | // Use USART1 with PA2 and PA3 as serial port 64 | let gpioa = p.GPIOA.split(); 65 | let tx = gpioa.pa2.into_alternate_af1(); 66 | let rx = gpioa.pa3.into_alternate_af1(); 67 | 68 | // Set up a timer expiring every millisecond 69 | let mut timer = p.TIM16.counter_ms(&rcc.clocks); 70 | 71 | // Generate an interrupt when the timer expires 72 | timer.listen(Event::Update); 73 | timer.start(1.millis()).unwrap(); 74 | 75 | cortex_m::interrupt::free(|cs| { 76 | // Move the timer into our global storage 77 | *GINT.borrow(cs).borrow_mut() = Some(timer); 78 | }); 79 | 80 | // Enable TIM1 IRQ, set prio 1 and clear any pending IRQs 81 | let mut nvic = cp.NVIC; 82 | unsafe { 83 | nvic.set_priority(Interrupt::TIM16, 1); 84 | cortex_m::peripheral::NVIC::unmask(Interrupt::TIM16); 85 | } 86 | cortex_m::peripheral::NVIC::unpend(Interrupt::TIM16); 87 | 88 | // Set up our serial port 89 | let mut serial = p.USART1.serial((tx, rx), 115_200.bps(), &rcc.clocks); 90 | 91 | // Print a welcome message 92 | writeln!( 93 | serial, 94 | "Welcome to the stop watch, hit any key to see the current value and 0 to reset\r", 95 | ) 96 | .ok(); 97 | 98 | loop { 99 | // Wait for reception of a single byte 100 | let received = nb::block!(serial.rx.read()).unwrap(); 101 | 102 | let time = cortex_m::interrupt::free(|cs| { 103 | let mut time = TIME.borrow(cs).borrow_mut(); 104 | 105 | // If we received a 0, reset the time 106 | if received == b'0' { 107 | time.millis = 0; 108 | time.seconds = 0; 109 | } 110 | 111 | *time 112 | }); 113 | 114 | // Print the current time 115 | writeln!(serial, "{}.{:03}s\r", time.seconds, time.millis).ok(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /examples/spi_hal_apa102c.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use py32f0xx_hal as hal; 7 | 8 | use crate::hal::{ 9 | pac, 10 | prelude::*, 11 | spi::{Mode, Phase, Polarity}, 12 | }; 13 | 14 | use cortex_m_rt::entry; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | const MODE: Mode = Mode { 19 | polarity: Polarity::IdleHigh, 20 | phase: Phase::CaptureOnSecondTransition, 21 | }; 22 | 23 | let p = pac::Peripherals::take().unwrap(); 24 | let mut flash = p.FLASH; 25 | let rcc = p.RCC.configure().freeze(&mut flash); 26 | 27 | let gpioa = p.GPIOA.split(); 28 | 29 | // Configure pins for SPI 30 | let (sck, miso, mosi) = ( 31 | gpioa.pa5.into_alternate_af0(), 32 | gpioa.pa6.into_alternate_af0(), 33 | gpioa.pa7.into_alternate_af0(), 34 | ); 35 | 36 | // Configure SPI with 100kHz rate 37 | let mut spi = p.SPI1.spi( 38 | (Some(sck), Some(miso), Some(mosi)), 39 | MODE, 40 | 100_000.Hz(), 41 | &rcc.clocks, 42 | ); 43 | 44 | // Cycle through colors on 16 chained APA102C LEDs 45 | loop { 46 | for r in 0..255 { 47 | let _ = spi.write(&[0, 0, 0, 0]); 48 | for _i in 0..16 { 49 | let _ = spi.write(&[0b1110_0001, 0, 0, r]); 50 | } 51 | let _ = spi.write(&[0xFF, 0xFF, 0xFF, 0xFF]); 52 | } 53 | for b in 0..255 { 54 | let _ = spi.write(&[0, 0, 0, 0]); 55 | for _i in 0..16 { 56 | let _ = spi.write(&[0b1110_0001, b, 0, 0]); 57 | } 58 | let _ = spi.write(&[0xFF, 0xFF, 0xFF, 0xFF]); 59 | } 60 | for g in 0..255 { 61 | let _ = spi.write(&[0, 0, 0, 0]); 62 | for _i in 0..16 { 63 | let _ = spi.write(&[0b1110_0001, 0, g, 0]); 64 | } 65 | let _ = spi.write(&[0xFF, 0xFF, 0xFF, 0xFF]); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/spi_rc522.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use defmt_rtt as _; 5 | use panic_probe as _; 6 | 7 | use py32f0xx_hal as hal; 8 | 9 | use crate::hal::{ 10 | pac, 11 | prelude::*, 12 | spi::{Mode, Phase, Polarity}, 13 | }; 14 | use cortex_m_rt::entry; 15 | use defmt::{error, info}; 16 | use embedded_hal_02::blocking::delay::DelayMs; 17 | 18 | use mfrc522::comm::eh02::spi::SpiInterface; 19 | use mfrc522::Mfrc522; 20 | 21 | /// A basic serial to spi example 22 | /// 23 | /// If you connect MOSI & MISO pins together, you'll see all characters 24 | /// that you typed in your serial terminal echoed back 25 | /// 26 | /// If you connect MISO to GND, you'll see nothing coming back 27 | #[entry] 28 | fn main() -> ! { 29 | const MODE: Mode = Mode { 30 | polarity: Polarity::IdleHigh, 31 | phase: Phase::CaptureOnSecondTransition, 32 | }; 33 | 34 | let p = pac::Peripherals::take().unwrap(); 35 | let cp = cortex_m::Peripherals::take().unwrap(); 36 | 37 | let mut flash = p.FLASH; 38 | let rcc = p.RCC.configure().freeze(&mut flash); 39 | 40 | let mut delay = cp.SYST.delay(&rcc.clocks); 41 | 42 | let gpioa = p.GPIOA.split(); 43 | 44 | let (sck, miso, mosi, nss, mut rst) = ( 45 | // SPI pins 46 | gpioa.pa5.into_alternate_af0(), 47 | gpioa.pa6.into_alternate_af0(), 48 | gpioa.pa7.into_alternate_af0(), 49 | // Aux pins 50 | gpioa.pa4.into_push_pull_output(), 51 | gpioa.pa1.into_push_pull_output(), 52 | ); 53 | rst.set_low(); 54 | 55 | // Configure SPI with 1MHz rate 56 | let spi = p.SPI1.spi( 57 | (Some(sck), Some(miso), Some(mosi)), 58 | MODE, 59 | 1.MHz(), 60 | &rcc.clocks, 61 | ); 62 | let itf = SpiInterface::new(spi).with_nss(nss).with_delay(|| { 63 | delay.delay_ms(1_u16); 64 | }); 65 | rst.set_high(); 66 | 67 | let mut mfrc522 = Mfrc522::new(itf).init().unwrap(); 68 | 69 | let ver = mfrc522.version().unwrap(); 70 | info!("MFRC522 version: 0x{:02x}", ver); 71 | assert!(ver == 0x91 || ver == 0x92); 72 | 73 | let mut timer = p.TIM1.counter_hz(&rcc.clocks); 74 | timer.start(1.Hz()).unwrap(); 75 | 76 | loop { 77 | info!("Waiting for card..."); 78 | match mfrc522.reqa() { 79 | Ok(atqa) => { 80 | if let Ok(uid) = mfrc522.select(&atqa) { 81 | info!("Selected card, UID: {:02X}", uid.as_bytes()); 82 | } else { 83 | error!("Failed to select card"); 84 | } 85 | } 86 | Err(_e) => error!("Error when requesting ATQA!"), 87 | } 88 | 89 | nb::block!(timer.wait()).unwrap(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /examples/watchdog.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use py32f0xx_hal as hal; 7 | 8 | use crate::hal::{pac, prelude::*, watchdog}; 9 | use core::fmt::Write; 10 | use cortex_m::peripheral::Peripherals; 11 | use cortex_m_rt::entry; 12 | use embedded_hal_02::blocking::delay::DelayMs; 13 | use embedded_hal_02::watchdog::{Watchdog, WatchdogEnable}; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | let p = pac::Peripherals::take().unwrap(); 18 | let cp = Peripherals::take().unwrap(); 19 | 20 | let mut flash = p.FLASH; 21 | let mut rcc = p.RCC.configure().sysclk(24.MHz()).freeze(&mut flash); 22 | 23 | let gpioa = p.GPIOA.split(); 24 | let dbg = p.DBG; 25 | 26 | // Disable the watchdog when the cpu is stopped under debug 27 | dbg.apb_fz1.modify(|_, w| w.dbg_iwdg_stop().set_bit()); 28 | 29 | let mut watchdog = watchdog::Watchdog::new(&mut rcc, p.IWDG); 30 | 31 | // Get delay provider 32 | let mut delay = cp.SYST.delay(&rcc.clocks); 33 | 34 | // Configure serial TX pin 35 | let tx = gpioa.pa2.into_alternate_af1(); 36 | 37 | // Obtain a serial peripheral with unidirectional communication 38 | let mut serial = p.USART1.tx(tx, 115_200.bps(), &rcc.clocks); 39 | 40 | serial.write_str("RESET \r\n").ok(); 41 | 42 | watchdog.start(1.Hz()); 43 | delay.delay_ms(500_u16); 44 | watchdog.feed(); 45 | serial.write_str("Feed the dog 1st\r\n").ok(); 46 | delay.delay_ms(500_u16); 47 | watchdog.feed(); 48 | serial.write_str("Feed the dog 2nd\r\n").ok(); 49 | delay.delay_ms(500_u16); 50 | watchdog.feed(); 51 | serial.write_str("Feed the dog 3rd\r\n").ok(); 52 | 53 | // Now a reset happens while delaying 54 | delay.delay_ms(1500_u16); 55 | serial.write_str("This won't\r\n").ok(); 56 | loop {} 57 | } 58 | -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 32K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 4K 6 | } 7 | 8 | /* This is where the call stack will be allocated. */ 9 | /* The stack is of the full descending type. */ 10 | /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ 11 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); 12 | -------------------------------------------------------------------------------- /openocd.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/cmsis-dap.cfg] 2 | source [find target/py32f0x.cfg] 3 | 4 | init 5 | flash probe 0 6 | -------------------------------------------------------------------------------- /openocd_program.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if (( $# != 1 )); then 3 | echo "Usage:" 4 | echo "$0 " 5 | exit 1 6 | fi 7 | 8 | openocd -f openocd.cfg -c "program $1 verify reset exit" 9 | -------------------------------------------------------------------------------- /src/adc.rs: -------------------------------------------------------------------------------- 1 | //! # API for the Analog to Digital converter 2 | //! 3 | //! Currently implements oneshot conversion with variable sampling times. 4 | //! Also references for the internal temperature sense, voltage 5 | //! reference and battery sense are provided. 6 | //! 7 | //! ## Example 8 | //! ``` no_run 9 | //! use py32f0xx_hal as hal; 10 | //! 11 | //! use crate::hal::pac; 12 | //! use crate::hal::prelude::*; 13 | //! use crate::hal::adc::Adc; 14 | //! 15 | //! let mut p = pac::Peripherals::take().unwrap(); 16 | //! let rcc = p.RCC.configure().freeze(&mut p.FLASH); 17 | //! 18 | //! let gpioa = p.GPIOA.split(); 19 | //! 20 | //! let mut led = gpioa.pa1.into_push_pull_pull_output(); 21 | //! let mut an_in = gpioa.pa0.into_analog(); 22 | //! 23 | //! let mut delay = Delay::new(cp.SYST, &rcc); 24 | //! 25 | //! let mut adc = Adc::new(p.ADC); 26 | //! 27 | //! loop { 28 | //! let val: u16 = adc.read(&mut an_in).unwrap(); 29 | //! if val < ((1 << 8) - 1) { 30 | //! led.set_low(); 31 | //! } else { 32 | //! led.set_high(); 33 | //! } 34 | //! delay.delay_ms(50_u16); 35 | //! } 36 | //! ``` 37 | 38 | // const VREFCAL: *const u16 = 0x1FFF_0F1A as *const u16; 39 | const VREFINT_VAL: u16 = 1200; 40 | const VTEMPCAL_LOW: *const u16 = 0x1FFF_0F14 as *const u16; 41 | const VTEMPCAL_HIGH: *const u16 = 0x1FFF_0F18 as *const u16; 42 | const VTEMPVAL_LOW: u16 = 30; 43 | const VTEMPVAL_HIGH: u16 = 85; 44 | const VTEMPVAL_DELTA: u16 = VTEMPVAL_HIGH - VTEMPVAL_LOW; 45 | 46 | use core::ptr; 47 | 48 | use embedded_hal_02::adc::{Channel, OneShot}; 49 | use embedded_hal_02::blocking::delay::DelayUs; 50 | 51 | use crate::{ 52 | gpio::*, 53 | pac::{ 54 | adc::{ 55 | cfgr1::{ALIGN_A, RES_A}, 56 | cfgr2::CKMODE_A, 57 | smpr::SMP_A, 58 | }, 59 | ADC, RCC, 60 | }, 61 | rcc::{Enable, Reset}, 62 | }; 63 | 64 | /// Analog to Digital converter interface 65 | pub struct Adc { 66 | rb: ADC, 67 | sample_time: AdcSampleTime, 68 | align: AdcAlign, 69 | precision: AdcPrecision, 70 | } 71 | 72 | /// ADC Clock mode, select adc clock source 73 | /// 74 | #[derive(Clone, Copy, Debug, PartialEq)] 75 | pub enum AdcClockMode { 76 | /// PCLK 77 | Pclk, 78 | /// PCLK/2 79 | PclkDiv2, 80 | /// PCLK/4 81 | PclkDiv4, 82 | /// PCLK/8 83 | PclkDiv8, 84 | /// PCLK/16 85 | PclkDiv16, 86 | /// PCLK/32 87 | PclkDiv32, 88 | /// PCLK/64 89 | PclkDiv64, 90 | /// HSI 91 | Hsi, 92 | /// HSI/2 93 | HsiDiv2, 94 | /// HSI/4 95 | HsiDiv4, 96 | /// HSI/8 97 | HsiDiv8, 98 | /// HSI/16 99 | HsiDiv16, 100 | /// HSI/32 101 | HsiDiv32, 102 | /// HSI/64 103 | HsiDiv64, 104 | } 105 | 106 | impl AdcClockMode { 107 | /// Get the default clock mode (currently PCLK) 108 | pub fn default() -> Self { 109 | AdcClockMode::Pclk 110 | } 111 | } 112 | 113 | impl From for CKMODE_A { 114 | fn from(value: AdcClockMode) -> Self { 115 | match value { 116 | AdcClockMode::Pclk => CKMODE_A::Pclk, 117 | AdcClockMode::PclkDiv2 => CKMODE_A::PclkDiv2, 118 | AdcClockMode::PclkDiv4 => CKMODE_A::PclkDiv4, 119 | AdcClockMode::PclkDiv8 => CKMODE_A::PclkDiv8, 120 | AdcClockMode::PclkDiv16 => CKMODE_A::PclkDiv16, 121 | AdcClockMode::PclkDiv32 => CKMODE_A::PclkDiv32, 122 | AdcClockMode::PclkDiv64 => CKMODE_A::PclkDiv64, 123 | AdcClockMode::Hsi => CKMODE_A::Hsi, 124 | AdcClockMode::HsiDiv2 => CKMODE_A::HsiDiv2, 125 | AdcClockMode::HsiDiv4 => CKMODE_A::HsiDiv4, 126 | AdcClockMode::HsiDiv8 => CKMODE_A::HsiDiv8, 127 | AdcClockMode::HsiDiv16 => CKMODE_A::HsiDiv16, 128 | AdcClockMode::HsiDiv32 => CKMODE_A::HsiDiv32, 129 | AdcClockMode::HsiDiv64 => CKMODE_A::HsiDiv64, 130 | } 131 | } 132 | } 133 | 134 | /// ADC Sampling time 135 | /// 136 | /// Options for the sampling time, each is T + 0.5 ADC clock cycles. 137 | #[derive(Copy, Clone, Debug, PartialEq)] 138 | pub enum AdcSampleTime { 139 | /// 3.5 cycles sampling time 140 | T_3, 141 | /// 5.5 cycles sampling time 142 | T_5, 143 | /// 7.5 cycles sampling time 144 | T_7, 145 | /// 13.5 cycles sampling time 146 | T_13, 147 | /// 28.5 cycles sampling time 148 | T_28, 149 | /// 41.5 cycles sampling time 150 | T_41, 151 | /// 71.5 cycles sampling time 152 | T_71, 153 | /// 239.5 cycles sampling time 154 | T_239, 155 | } 156 | 157 | impl AdcSampleTime { 158 | /// Get the default sample time (currently 239.5 cycles) 159 | pub fn default() -> Self { 160 | AdcSampleTime::T_239 161 | } 162 | } 163 | 164 | impl From for SMP_A { 165 | fn from(val: AdcSampleTime) -> Self { 166 | match val { 167 | AdcSampleTime::T_3 => SMP_A::Cycles35, 168 | AdcSampleTime::T_5 => SMP_A::Cycles55, 169 | AdcSampleTime::T_7 => SMP_A::Cycles75, 170 | AdcSampleTime::T_13 => SMP_A::Cycles135, 171 | AdcSampleTime::T_28 => SMP_A::Cycles285, 172 | AdcSampleTime::T_41 => SMP_A::Cycles415, 173 | AdcSampleTime::T_71 => SMP_A::Cycles715, 174 | AdcSampleTime::T_239 => SMP_A::Cycles2395, 175 | } 176 | } 177 | } 178 | 179 | /// ADC Result Alignment 180 | #[derive(Copy, Clone, Debug, PartialEq)] 181 | pub enum AdcAlign { 182 | /// Left aligned results (most significant bits) 183 | /// 184 | /// Results in all precisions returning a value in the range 0-65535. 185 | /// Depending on the precision the result will step by larger or smaller 186 | /// amounts. 187 | Left, 188 | /// Right aligned results (least significant bits) 189 | /// 190 | /// Results in all precisions returning values from 0-(2^bits-1) in 191 | /// steps of 1. 192 | Right, 193 | /// Left aligned results without correction of 6bit values. 194 | /// 195 | /// Returns left aligned results exactly as shown in RM0091 Fig.37. 196 | /// Where the values are left aligned within the u16, with the exception 197 | /// of 6 bit mode where the value is left aligned within the first byte of 198 | /// the u16. 199 | LeftAsRM, 200 | } 201 | 202 | impl AdcAlign { 203 | /// Get the default alignment (currently right aligned) 204 | pub fn default() -> Self { 205 | AdcAlign::Right 206 | } 207 | } 208 | 209 | impl From for ALIGN_A { 210 | fn from(val: AdcAlign) -> Self { 211 | match val { 212 | AdcAlign::Left => ALIGN_A::Left, 213 | AdcAlign::Right => ALIGN_A::Right, 214 | AdcAlign::LeftAsRM => ALIGN_A::Left, 215 | } 216 | } 217 | } 218 | 219 | /// ADC Sampling Precision 220 | #[derive(Copy, Clone, Debug, PartialEq)] 221 | pub enum AdcPrecision { 222 | /// 12 bit precision 223 | B_12, 224 | /// 10 bit precision 225 | B_10, 226 | /// 8 bit precision 227 | B_8, 228 | /// 6 bit precision 229 | B_6, 230 | } 231 | 232 | impl AdcPrecision { 233 | /// Get the default precision (currently 12 bit precision) 234 | pub fn default() -> Self { 235 | AdcPrecision::B_12 236 | } 237 | } 238 | 239 | impl From for RES_A { 240 | fn from(val: AdcPrecision) -> Self { 241 | match val { 242 | AdcPrecision::B_12 => RES_A::TwelveBit, 243 | AdcPrecision::B_10 => RES_A::TenBit, 244 | AdcPrecision::B_8 => RES_A::EightBit, 245 | AdcPrecision::B_6 => RES_A::SixBit, 246 | } 247 | } 248 | } 249 | 250 | macro_rules! adc_pins { 251 | ($($pin:ty => $chan:expr),+ $(,)*) => { 252 | $( 253 | impl Channel for $pin { 254 | type ID = u8; 255 | 256 | fn channel() -> u8 { $chan } 257 | } 258 | )+ 259 | }; 260 | } 261 | 262 | adc_pins!( 263 | gpioa::PA0 => 0_u8, 264 | gpioa::PA1 => 1_u8, 265 | gpioa::PA2 => 2_u8, 266 | gpioa::PA3 => 3_u8, 267 | gpioa::PA4 => 4_u8, 268 | gpioa::PA5 => 5_u8, 269 | gpioa::PA6 => 6_u8, 270 | gpioa::PA7 => 7_u8, 271 | gpiob::PB0 => 8_u8, 272 | gpiob::PB1 => 9_u8, 273 | ); 274 | 275 | #[derive(Debug, Default)] 276 | /// Internal temperature sensor (ADC Channel 11) 277 | pub struct VTemp; 278 | 279 | #[derive(Debug, Default)] 280 | /// Internal voltage reference (ADC Channel 12) 281 | pub struct VRef; 282 | 283 | adc_pins!( 284 | VTemp => 11_u8, 285 | VRef => 12_u8, 286 | ); 287 | 288 | impl VTemp { 289 | /// Init a new VTemp 290 | pub fn new() -> Self { 291 | VTemp::default() 292 | } 293 | 294 | /// Enable the internal temperature sense, this has a wake up time 295 | /// tSTART which can be found in your micro's datasheet, you 296 | /// must wait at least that long after enabling before taking a reading. 297 | /// Remember to disable when not in use. 298 | pub fn enable(&mut self, adc: &mut Adc) { 299 | adc.rb.ccr.modify(|_, w| w.tsen().set_bit()); 300 | } 301 | 302 | /// Disable the internal temperature sense. 303 | pub fn disable(&mut self, adc: &mut Adc) { 304 | adc.rb.ccr.modify(|_, w| w.tsen().clear_bit()); 305 | } 306 | 307 | /// Checks if the temperature sensor is enabled, does not account for the 308 | /// tSTART time however. 309 | pub fn is_enabled(&self, adc: &Adc) -> bool { 310 | adc.rb.ccr.read().tsen().bit_is_set() 311 | } 312 | 313 | fn convert_temp(vtemp: u16) -> i16 { 314 | let vtempcal_low = unsafe { ptr::read(VTEMPCAL_LOW) } as i32; 315 | let vtempcal_high = unsafe { ptr::read(VTEMPCAL_HIGH) } as i32; 316 | ((vtemp as i32 - vtempcal_low) * 100 * (VTEMPVAL_DELTA as i32) 317 | / (vtempcal_high - vtempcal_low) 318 | + 3000) as i16 319 | } 320 | 321 | /// Read the value of the internal temperature sensor and return the 322 | /// result in 10ths of a degree centigrade. 323 | /// 324 | /// Given a delay reference it will attempt to restrict to the 325 | /// minimum delay needed to ensure a 10 us tSTART value. 326 | /// Otherwise it will approximate the required delay using ADC reads. 327 | pub fn read(adc: &mut Adc, delay: Option<&mut dyn DelayUs>) -> i16 { 328 | let mut vtemp = Self::new(); 329 | let vtemp_preenable = vtemp.is_enabled(adc); 330 | 331 | if !vtemp_preenable { 332 | vtemp.enable(adc); 333 | 334 | if let Some(dref) = delay { 335 | dref.delay_us(2); 336 | } else { 337 | // Double read of vdda to allow sufficient startup time for the temp sensor 338 | VRef::read_vdda(adc); 339 | } 340 | } 341 | // let vdda = VRef::read_vdda(adc); 342 | 343 | let prev_cfg = adc.default_cfg(); 344 | 345 | let vtemp_val = adc.read(&mut vtemp).unwrap(); 346 | 347 | if !vtemp_preenable { 348 | vtemp.disable(adc); 349 | } 350 | 351 | adc.restore_cfg(prev_cfg); 352 | 353 | Self::convert_temp(vtemp_val) 354 | } 355 | } 356 | 357 | impl VRef { 358 | /// Init a new VRef 359 | pub fn new() -> Self { 360 | VRef::default() 361 | } 362 | 363 | /// Enable the internal voltage reference, remember to disable when not in use. 364 | pub fn enable(&mut self, adc: &mut Adc) { 365 | adc.rb.ccr.modify(|_, w| w.vrefen().set_bit()); 366 | } 367 | 368 | /// Disable the internal reference voltage. 369 | pub fn disable(&mut self, adc: &mut Adc) { 370 | adc.rb.ccr.modify(|_, w| w.vrefen().clear_bit()); 371 | } 372 | 373 | /// Returns if the internal voltage reference is enabled. 374 | pub fn is_enabled(&self, adc: &Adc) -> bool { 375 | adc.rb.ccr.read().vrefen().bit_is_set() 376 | } 377 | 378 | /// Reads the value of VDDA in milli-volts 379 | pub fn read_vdda(adc: &mut Adc) -> u16 { 380 | let mut vref = Self::new(); 381 | 382 | let prev_cfg = adc.default_cfg(); 383 | 384 | let vref_val: u32 = if vref.is_enabled(adc) { 385 | adc.read(&mut vref).unwrap() 386 | } else { 387 | vref.enable(adc); 388 | 389 | let ret = adc.read(&mut vref).unwrap(); 390 | 391 | vref.disable(adc); 392 | ret 393 | }; 394 | 395 | adc.restore_cfg(prev_cfg); 396 | 397 | ((u32::from(VREFINT_VAL) * 4095) / vref_val) as u16 398 | } 399 | } 400 | 401 | /// A stored ADC config, can be restored by using the `Adc::restore_cfg` method 402 | #[derive(Copy, Clone, Debug, PartialEq)] 403 | pub struct StoredConfig(AdcSampleTime, AdcAlign, AdcPrecision); 404 | 405 | impl Adc { 406 | /// Init a new Adc 407 | /// 408 | /// Sets all configurable parameters to defaults, enables the HSI14 clock 409 | /// for the ADC if it is not already enabled and performs a boot time 410 | /// calibration. As such this method may take an appreciable time to run. 411 | pub fn new(adc: ADC, ckmode: AdcClockMode) -> Self { 412 | let mut s = Self { 413 | rb: adc, 414 | sample_time: AdcSampleTime::default(), 415 | align: AdcAlign::default(), 416 | precision: AdcPrecision::default(), 417 | }; 418 | s.select_clock(ckmode); 419 | s.calibrate(); 420 | s 421 | } 422 | 423 | /// Saves a copy of the current ADC config 424 | pub fn save_cfg(&mut self) -> StoredConfig { 425 | StoredConfig(self.sample_time, self.align, self.precision) 426 | } 427 | 428 | /// Restores a stored config 429 | pub fn restore_cfg(&mut self, cfg: StoredConfig) { 430 | self.sample_time = cfg.0; 431 | self.align = cfg.1; 432 | self.precision = cfg.2; 433 | } 434 | 435 | /// Resets the ADC config to default, returning the existing config as 436 | /// a stored config. 437 | pub fn default_cfg(&mut self) -> StoredConfig { 438 | let cfg = self.save_cfg(); 439 | self.sample_time = AdcSampleTime::default(); 440 | self.align = AdcAlign::default(); 441 | self.precision = AdcPrecision::default(); 442 | cfg 443 | } 444 | 445 | /// Set the Adc sampling time 446 | /// 447 | /// Options can be found in [AdcSampleTime] 448 | pub fn set_sample_time(&mut self, t_samp: AdcSampleTime) { 449 | self.sample_time = t_samp; 450 | } 451 | 452 | /// Set the Adc result alignment 453 | /// 454 | /// Options can be found in [AdcAlign] 455 | pub fn set_align(&mut self, align: AdcAlign) { 456 | self.align = align; 457 | } 458 | 459 | /// Set the Adc precision 460 | /// 461 | /// Options can be found in [AdcPrecision] 462 | pub fn set_precision(&mut self, precision: AdcPrecision) { 463 | self.precision = precision; 464 | } 465 | 466 | /// Returns the largest possible sample value for the current settings 467 | pub fn max_sample(&self) -> u16 { 468 | match self.align { 469 | AdcAlign::Left => u16::max_value(), 470 | AdcAlign::LeftAsRM => match self.precision { 471 | AdcPrecision::B_6 => u16::from(u8::max_value()), 472 | _ => u16::max_value(), 473 | }, 474 | AdcAlign::Right => match self.precision { 475 | AdcPrecision::B_12 => (1 << 12) - 1, 476 | AdcPrecision::B_10 => (1 << 10) - 1, 477 | AdcPrecision::B_8 => (1 << 8) - 1, 478 | AdcPrecision::B_6 => (1 << 6) - 1, 479 | }, 480 | } 481 | } 482 | 483 | /// Read the value of a channel and converts the result to milli-volts 484 | pub fn read_abs_mv>(&mut self, pin: &mut PIN) -> u16 { 485 | let vdda = u32::from(VRef::read_vdda(self)); 486 | let val: u32 = self.read(pin).unwrap(); 487 | let max_samp = u32::from(self.max_sample()); 488 | 489 | (val * vdda / max_samp) as u16 490 | } 491 | 492 | fn calibrate(&mut self) { 493 | /* Ensure that ADEN = 0 */ 494 | if self.rb.cr.read().aden().is_enabled() { 495 | /* Clear ADEN by stop conversion */ 496 | self.rb.cr.modify(|_, w| w.adstp().stop_conversion()); 497 | while self.rb.cr.read().adstp().is_stopping() {} 498 | } 499 | while self.rb.cr.read().aden().is_enabled() {} 500 | 501 | // get and save DMAEN and DMACFG 502 | #[cfg(feature = "with-dma")] 503 | let (dmacfg, dmaen) = { 504 | let dmacfg = self.rb.cfgr1.read().dmacfg().bit(); 505 | let dmaen = self.rb.cfgr1.read().dmaen().is_enabled(); 506 | 507 | (dmacfg, dmaen) 508 | }; 509 | 510 | /* Clear DMAEN */ 511 | #[cfg(feature = "with-dma")] 512 | self.rb.cfgr1.modify(|_, w| w.dmaen().disabled()); 513 | 514 | /* Start calibration by setting ADCAL */ 515 | self.rb.cr.modify(|_, w| w.adcal().start_calibration()); 516 | 517 | /* Wait until calibration is finished and ADCAL = 0 */ 518 | while self.rb.cr.read().adcal().is_calibrating() {} 519 | 520 | // restore DMAEN and DMACFG to original values 521 | #[cfg(feature = "with-dma")] 522 | self.rb 523 | .cfgr1 524 | .modify(|_, w| w.dmacfg().bit(dmacfg).dmaen().bit(dmaen)); 525 | } 526 | 527 | fn select_clock(&mut self, ckmode: AdcClockMode) { 528 | let rcc = unsafe { &(*RCC::ptr()) }; 529 | ADC::reset(rcc); 530 | ADC::enable(rcc); 531 | self.rb 532 | .cfgr2 533 | .modify(|_, w| w.ckmode().variant(ckmode.into())); 534 | } 535 | 536 | /// Apply config settings 537 | fn apply_cfg(&mut self) { 538 | self.rb 539 | .smpr 540 | .write(|w| w.smp().variant(self.sample_time.into())); 541 | self.rb.cfgr1.modify(|_, w| { 542 | w.res() 543 | .variant(self.precision.into()) 544 | .align() 545 | .variant(self.align.into()) 546 | .wait() 547 | .disabled() 548 | }); 549 | } 550 | 551 | fn power_up(&mut self, chan: u8) { 552 | self.apply_cfg(); 553 | self.rb.chselr.write(|w| unsafe { w.bits(1_u32 << chan) }); 554 | self.rb.cr.modify(|_, w| w.aden().enabled()); 555 | } 556 | 557 | fn power_down(&mut self) { 558 | if self.rb.cr.read().aden().is_enabled() { 559 | self.rb.cr.modify(|_, w| w.adstp().stop_conversion()); 560 | while self.rb.cr.read().adstp().is_stopping() {} 561 | } 562 | } 563 | 564 | fn convert(&mut self) -> u16 { 565 | self.rb.cr.modify(|_, w| w.adstart().start_conversion()); 566 | while self.rb.isr.read().eoc().is_not_complete() {} 567 | 568 | let res = self.rb.dr.read().bits() as u16; 569 | if self.align == AdcAlign::Left && self.precision == AdcPrecision::B_6 { 570 | res << 8 571 | } else { 572 | res 573 | } 574 | } 575 | } 576 | 577 | impl OneShot for Adc 578 | where 579 | WORD: From, 580 | PIN: Channel, 581 | { 582 | type Error = (); 583 | 584 | fn read(&mut self, _pin: &mut PIN) -> nb::Result { 585 | self.power_up(PIN::channel()); 586 | let res = self.convert(); 587 | self.power_down(); 588 | Ok(res.into()) 589 | } 590 | } 591 | -------------------------------------------------------------------------------- /src/gpio/erased.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Type alias for an [ErasedPin] 4 | pub type AnyPin = ErasedPin; 5 | 6 | macro_rules! impl_pxx { 7 | ($(($port_id:literal :: $pin:ident)),*) => { 8 | /// Erased pin 9 | /// 10 | /// `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). 11 | pub enum ErasedPin { 12 | $( 13 | /// Partially erased pin 14 | $pin(PartiallyErasedPin<$port_id, MODE>) 15 | ),* 16 | } 17 | 18 | impl PinExt for ErasedPin { 19 | type Mode = MODE; 20 | 21 | #[inline(always)] 22 | fn pin_id(&self) -> u8 { 23 | match self { 24 | $(Self::$pin(pin) => pin.pin_id()),* 25 | } 26 | } 27 | 28 | #[inline(always)] 29 | fn port_id(&self) -> u8 { 30 | match self { 31 | $(Self::$pin(pin) => pin.port_id()),* 32 | } 33 | } 34 | } 35 | 36 | impl ErasedPin> { 37 | /// Set a pin to high level 38 | pub fn set_high(&mut self) { 39 | match self { 40 | $(Self::$pin(pin) => pin.set_high()),* 41 | } 42 | } 43 | /// Set a pin to low level 44 | pub fn set_low(&mut self) { 45 | match self { 46 | $(Self::$pin(pin) => pin.set_low()),* 47 | } 48 | } 49 | /// returns true if pin is set to high level 50 | pub fn is_set_high(&self) -> bool { 51 | match self { 52 | $(Self::$pin(pin) => pin.is_set_high()),* 53 | } 54 | } 55 | /// returns true if pin is set to low level 56 | pub fn is_set_low(&self) -> bool { 57 | match self { 58 | $(Self::$pin(pin) => pin.is_set_low()),* 59 | } 60 | } 61 | } 62 | 63 | impl ErasedPin> { 64 | /// returns true if pin is at high level 65 | pub fn is_high(&self) -> bool { 66 | match self { 67 | $(Self::$pin(pin) => pin.is_high()),* 68 | } 69 | } 70 | /// returns true if pin is at low level 71 | pub fn is_low(&self) -> bool { 72 | match self { 73 | $(Self::$pin(pin) => pin.is_low()),* 74 | } 75 | } 76 | } 77 | 78 | impl ErasedPin> { 79 | /// returns true if pin is at high level 80 | pub fn is_high(&self) -> bool { 81 | match self { 82 | $(Self::$pin(pin) => pin.is_high()),* 83 | } 84 | } 85 | 86 | /// returns true if pin is at low level 87 | pub fn is_low(&self) -> bool { 88 | match self { 89 | $(Self::$pin(pin) => pin.is_low()),* 90 | } 91 | } 92 | } 93 | } 94 | } 95 | 96 | impl ErasedPin> { 97 | /// Get the [PinState] of a pin 98 | #[inline(always)] 99 | pub fn get_state(&self) -> PinState { 100 | if self.is_set_low() { 101 | PinState::Low 102 | } else { 103 | PinState::High 104 | } 105 | } 106 | 107 | /// Set the [PinState] of a pin 108 | #[inline(always)] 109 | pub fn set_state(&mut self, state: PinState) { 110 | match state { 111 | PinState::Low => self.set_low(), 112 | PinState::High => self.set_high(), 113 | } 114 | } 115 | 116 | /// Toggle the pin from a high level to low level, or the reverse 117 | #[inline(always)] 118 | pub fn toggle(&mut self) { 119 | if self.is_set_low() { 120 | self.set_high() 121 | } else { 122 | self.set_low() 123 | } 124 | } 125 | } 126 | 127 | #[cfg(any(feature = "py32f030", feature = "py32f003", feature = "py32f002a"))] 128 | impl_pxx! { 129 | ('A'::PAx), 130 | ('B'::PBx), 131 | ('F'::PFx) 132 | } 133 | 134 | #[cfg(feature = "py32f002b")] 135 | impl_pxx! { 136 | ('A'::PAx), 137 | ('B'::PBx), 138 | ('C'::PCx) 139 | } 140 | -------------------------------------------------------------------------------- /src/gpio/hal_02.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use core::convert::Infallible; 3 | use embedded_hal_02::digital::v2::{toggleable, InputPin, OutputPin, StatefulOutputPin}; 4 | 5 | // Pin 6 | 7 | impl OutputPin for Pin { 8 | type Error = PinModeError; 9 | fn set_high(&mut self) -> Result<(), Self::Error> { 10 | if self.mode.is_output() { 11 | self._set_state(PinState::High); 12 | Ok(()) 13 | } else { 14 | Err(PinModeError::IncorrectMode) 15 | } 16 | } 17 | fn set_low(&mut self) -> Result<(), Self::Error> { 18 | if self.mode.is_output() { 19 | self._set_state(PinState::Low); 20 | Ok(()) 21 | } else { 22 | Err(PinModeError::IncorrectMode) 23 | } 24 | } 25 | } 26 | 27 | impl InputPin for Pin { 28 | type Error = PinModeError; 29 | fn is_high(&self) -> Result { 30 | self.is_low().map(|b| !b) 31 | } 32 | fn is_low(&self) -> Result { 33 | if self.mode.is_input() { 34 | Ok(self._is_low()) 35 | } else { 36 | Err(PinModeError::IncorrectMode) 37 | } 38 | } 39 | } 40 | 41 | impl OutputPin for Pin> { 42 | type Error = Infallible; 43 | #[inline] 44 | fn set_high(&mut self) -> Result<(), Self::Error> { 45 | self.set_high(); 46 | Ok(()) 47 | } 48 | #[inline] 49 | fn set_low(&mut self) -> Result<(), Self::Error> { 50 | self.set_low(); 51 | Ok(()) 52 | } 53 | } 54 | 55 | impl StatefulOutputPin for Pin> { 56 | #[inline] 57 | fn is_set_high(&self) -> Result { 58 | Ok(self.is_set_high()) 59 | } 60 | #[inline] 61 | fn is_set_low(&self) -> Result { 62 | Ok(self.is_set_low()) 63 | } 64 | } 65 | 66 | impl InputPin for Pin> { 67 | type Error = Infallible; 68 | #[inline] 69 | fn is_high(&self) -> Result { 70 | Ok(self.is_high()) 71 | } 72 | 73 | #[inline] 74 | fn is_low(&self) -> Result { 75 | Ok(self.is_low()) 76 | } 77 | } 78 | 79 | impl InputPin for Pin> { 80 | type Error = Infallible; 81 | #[inline] 82 | fn is_high(&self) -> Result { 83 | Ok(self.is_high()) 84 | } 85 | 86 | #[inline] 87 | fn is_low(&self) -> Result { 88 | Ok(self.is_low()) 89 | } 90 | } 91 | 92 | impl toggleable::Default for Pin> {} 93 | 94 | // PartiallyErasedPin 95 | 96 | impl OutputPin for PartiallyErasedPin> { 97 | type Error = Infallible; 98 | 99 | #[inline(always)] 100 | fn set_high(&mut self) -> Result<(), Self::Error> { 101 | self.set_high(); 102 | Ok(()) 103 | } 104 | 105 | #[inline(always)] 106 | fn set_low(&mut self) -> Result<(), Self::Error> { 107 | self.set_low(); 108 | Ok(()) 109 | } 110 | } 111 | 112 | impl StatefulOutputPin for PartiallyErasedPin> { 113 | #[inline(always)] 114 | fn is_set_high(&self) -> Result { 115 | Ok(self.is_set_high()) 116 | } 117 | 118 | #[inline(always)] 119 | fn is_set_low(&self) -> Result { 120 | Ok(self.is_set_low()) 121 | } 122 | } 123 | 124 | impl InputPin for PartiallyErasedPin> { 125 | type Error = Infallible; 126 | 127 | #[inline(always)] 128 | fn is_high(&self) -> Result { 129 | Ok(self.is_high()) 130 | } 131 | 132 | #[inline(always)] 133 | fn is_low(&self) -> Result { 134 | Ok(self.is_low()) 135 | } 136 | } 137 | 138 | impl InputPin for PartiallyErasedPin> { 139 | type Error = Infallible; 140 | 141 | #[inline(always)] 142 | fn is_high(&self) -> Result { 143 | Ok(self.is_high()) 144 | } 145 | 146 | #[inline(always)] 147 | fn is_low(&self) -> Result { 148 | Ok(self.is_low()) 149 | } 150 | } 151 | 152 | impl toggleable::Default for PartiallyErasedPin> {} 153 | 154 | // ErasedPin 155 | 156 | impl OutputPin for ErasedPin> { 157 | type Error = Infallible; 158 | fn set_high(&mut self) -> Result<(), Infallible> { 159 | self.set_high(); 160 | Ok(()) 161 | } 162 | 163 | fn set_low(&mut self) -> Result<(), Infallible> { 164 | self.set_low(); 165 | Ok(()) 166 | } 167 | } 168 | 169 | impl StatefulOutputPin for ErasedPin> { 170 | fn is_set_high(&self) -> Result { 171 | Ok(self.is_set_high()) 172 | } 173 | 174 | fn is_set_low(&self) -> Result { 175 | Ok(self.is_set_low()) 176 | } 177 | } 178 | 179 | impl InputPin for ErasedPin> { 180 | type Error = Infallible; 181 | fn is_high(&self) -> Result { 182 | Ok(self.is_high()) 183 | } 184 | 185 | fn is_low(&self) -> Result { 186 | Ok(self.is_low()) 187 | } 188 | } 189 | 190 | impl InputPin for ErasedPin> { 191 | type Error = Infallible; 192 | fn is_high(&self) -> Result { 193 | Ok(self.is_high()) 194 | } 195 | 196 | fn is_low(&self) -> Result { 197 | Ok(self.is_low()) 198 | } 199 | } 200 | 201 | impl toggleable::Default for ErasedPin> {} 202 | -------------------------------------------------------------------------------- /src/gpio/hal_1.rs: -------------------------------------------------------------------------------- 1 | use super::{Dynamic, ErasedPin, Input, OpenDrain, Output, PartiallyErasedPin, Pin, PinModeError}; 2 | use core::convert::Infallible; 3 | pub use embedded_hal::digital::PinState; 4 | use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin}; 5 | 6 | fn into_state(state: PinState) -> super::PinState { 7 | match state { 8 | PinState::Low => super::PinState::Low, 9 | PinState::High => super::PinState::High, 10 | } 11 | } 12 | 13 | // Implementations for `Pin` 14 | 15 | impl ErrorType for Pin> { 16 | type Error = Infallible; 17 | } 18 | impl ErrorType for Pin> { 19 | type Error = Infallible; 20 | } 21 | 22 | impl embedded_hal::digital::Error for PinModeError { 23 | fn kind(&self) -> embedded_hal::digital::ErrorKind { 24 | match self { 25 | PinModeError::IncorrectMode => embedded_hal::digital::ErrorKind::Other, 26 | } 27 | } 28 | } 29 | 30 | impl ErrorType for Pin { 31 | type Error = PinModeError; 32 | } 33 | 34 | impl OutputPin for Pin { 35 | fn set_high(&mut self) -> Result<(), Self::Error> { 36 | if self.mode.is_output() { 37 | self._set_state(into_state(PinState::High)); 38 | Ok(()) 39 | } else { 40 | Err(PinModeError::IncorrectMode) 41 | } 42 | } 43 | fn set_low(&mut self) -> Result<(), Self::Error> { 44 | if self.mode.is_output() { 45 | self._set_state(into_state(PinState::Low)); 46 | Ok(()) 47 | } else { 48 | Err(PinModeError::IncorrectMode) 49 | } 50 | } 51 | } 52 | 53 | impl InputPin for Pin { 54 | fn is_high(&mut self) -> Result { 55 | self.is_low().map(|b| !b) 56 | } 57 | fn is_low(&mut self) -> Result { 58 | if self.mode.is_input() { 59 | Ok(self._is_low()) 60 | } else { 61 | Err(PinModeError::IncorrectMode) 62 | } 63 | } 64 | } 65 | 66 | impl StatefulOutputPin for Pin> { 67 | #[inline(always)] 68 | fn is_set_high(&mut self) -> Result { 69 | Ok((*self).is_set_high()) 70 | } 71 | 72 | #[inline(always)] 73 | fn is_set_low(&mut self) -> Result { 74 | Ok((*self).is_set_low()) 75 | } 76 | } 77 | 78 | impl OutputPin for Pin> { 79 | #[inline(always)] 80 | fn set_high(&mut self) -> Result<(), Self::Error> { 81 | self.set_high(); 82 | Ok(()) 83 | } 84 | 85 | #[inline(always)] 86 | fn set_low(&mut self) -> Result<(), Self::Error> { 87 | self.set_low(); 88 | Ok(()) 89 | } 90 | } 91 | 92 | impl InputPin for Pin> { 93 | #[inline(always)] 94 | fn is_high(&mut self) -> Result { 95 | Ok((*self).is_high()) 96 | } 97 | 98 | #[inline(always)] 99 | fn is_low(&mut self) -> Result { 100 | Ok((*self).is_low()) 101 | } 102 | } 103 | 104 | impl InputPin for Pin> { 105 | #[inline(always)] 106 | fn is_high(&mut self) -> Result { 107 | Ok((*self).is_high()) 108 | } 109 | 110 | #[inline(always)] 111 | fn is_low(&mut self) -> Result { 112 | Ok((*self).is_low()) 113 | } 114 | } 115 | 116 | // PartiallyErasedPin 117 | 118 | impl ErrorType for PartiallyErasedPin { 119 | type Error = Infallible; 120 | } 121 | 122 | impl OutputPin for PartiallyErasedPin> { 123 | #[inline(always)] 124 | fn set_high(&mut self) -> Result<(), Self::Error> { 125 | self.set_high(); 126 | Ok(()) 127 | } 128 | 129 | #[inline(always)] 130 | fn set_low(&mut self) -> Result<(), Self::Error> { 131 | self.set_low(); 132 | Ok(()) 133 | } 134 | } 135 | 136 | impl StatefulOutputPin for PartiallyErasedPin> { 137 | #[inline(always)] 138 | fn is_set_high(&mut self) -> Result { 139 | Ok((*self).is_set_high()) 140 | } 141 | 142 | #[inline(always)] 143 | fn is_set_low(&mut self) -> Result { 144 | Ok((*self).is_set_low()) 145 | } 146 | } 147 | 148 | impl InputPin for PartiallyErasedPin> { 149 | #[inline(always)] 150 | fn is_high(&mut self) -> Result { 151 | Ok((*self).is_high()) 152 | } 153 | 154 | #[inline(always)] 155 | fn is_low(&mut self) -> Result { 156 | Ok((*self).is_low()) 157 | } 158 | } 159 | 160 | impl InputPin for PartiallyErasedPin> { 161 | #[inline(always)] 162 | fn is_high(&mut self) -> Result { 163 | Ok((*self).is_high()) 164 | } 165 | 166 | #[inline(always)] 167 | fn is_low(&mut self) -> Result { 168 | Ok((*self).is_low()) 169 | } 170 | } 171 | 172 | // ErasedPin 173 | 174 | impl ErrorType for ErasedPin { 175 | type Error = core::convert::Infallible; 176 | } 177 | 178 | impl OutputPin for ErasedPin> { 179 | fn set_high(&mut self) -> Result<(), Infallible> { 180 | self.set_high(); 181 | Ok(()) 182 | } 183 | 184 | fn set_low(&mut self) -> Result<(), Infallible> { 185 | self.set_low(); 186 | Ok(()) 187 | } 188 | } 189 | 190 | impl StatefulOutputPin for ErasedPin> { 191 | fn is_set_high(&mut self) -> Result { 192 | Ok((*self).is_set_high()) 193 | } 194 | 195 | fn is_set_low(&mut self) -> Result { 196 | Ok((*self).is_set_low()) 197 | } 198 | } 199 | 200 | impl InputPin for ErasedPin> { 201 | fn is_high(&mut self) -> Result { 202 | Ok((*self).is_high()) 203 | } 204 | 205 | fn is_low(&mut self) -> Result { 206 | Ok((*self).is_low()) 207 | } 208 | } 209 | 210 | impl InputPin for ErasedPin> { 211 | fn is_high(&mut self) -> Result { 212 | Ok((*self).is_high()) 213 | } 214 | 215 | fn is_low(&mut self) -> Result { 216 | Ok((*self).is_low()) 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/gpio/partially_erased.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// type alias for a [PartiallyErasedPin] 4 | pub type PEPin = PartiallyErasedPin; 5 | 6 | /// Partially erased pin 7 | /// 8 | /// - `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). 9 | /// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc. 10 | pub struct PartiallyErasedPin { 11 | pin_number: u8, 12 | _mode: PhantomData, 13 | } 14 | 15 | impl PartiallyErasedPin { 16 | pub(crate) fn new(pin_number: u8) -> Self { 17 | Self { 18 | pin_number, 19 | _mode: PhantomData, 20 | } 21 | } 22 | } 23 | 24 | impl PinExt for PartiallyErasedPin { 25 | type Mode = MODE; 26 | 27 | #[inline(always)] 28 | fn pin_id(&self) -> u8 { 29 | self.pin_number 30 | } 31 | 32 | #[inline(always)] 33 | fn port_id(&self) -> u8 { 34 | P as u8 35 | } 36 | } 37 | 38 | impl PartiallyErasedPin> { 39 | /// Set a pin to high level 40 | #[inline(always)] 41 | pub fn set_high(&mut self) { 42 | // NOTE(unsafe) atomic write to a stateless register 43 | let gpio = unsafe { &(*gpiox::

()) }; 44 | unsafe { gpio.bsrr.write(|w| w.bits(1 << self.pin_number)) } 45 | } 46 | /// Set a pin to low level 47 | #[inline(always)] 48 | pub fn set_low(&mut self) { 49 | // NOTE(unsafe) atomic write to a stateless register 50 | let gpio = unsafe { &(*gpiox::

()) }; 51 | unsafe { gpio.bsrr.write(|w| w.bits(1 << (self.pin_number + 16))) } 52 | } 53 | /// Get the [PinState] for a pin 54 | #[inline(always)] 55 | pub fn get_state(&self) -> PinState { 56 | if self.is_set_low() { 57 | PinState::Low 58 | } else { 59 | PinState::High 60 | } 61 | } 62 | /// Set the [PinState] for a pin 63 | #[inline(always)] 64 | pub fn set_state(&mut self, state: PinState) { 65 | match state { 66 | PinState::Low => self.set_low(), 67 | PinState::High => self.set_high(), 68 | } 69 | } 70 | /// returns true if pin is set to high level 71 | #[inline(always)] 72 | pub fn is_set_high(&self) -> bool { 73 | !self.is_set_low() 74 | } 75 | /// returns true if pin is set to low level 76 | #[inline(always)] 77 | pub fn is_set_low(&self) -> bool { 78 | // NOTE(unsafe) atomic read with no side effects 79 | let gpio = unsafe { &(*gpiox::

()) }; 80 | gpio.odr.read().bits() & (1 << self.pin_number) == 0 81 | } 82 | /// Toggle the pin state from high level to low level, or the reverse 83 | #[inline(always)] 84 | pub fn toggle(&mut self) { 85 | if self.is_set_low() { 86 | self.set_high() 87 | } else { 88 | self.set_low() 89 | } 90 | } 91 | } 92 | 93 | impl PartiallyErasedPin> { 94 | /// returns true if pin is at high level 95 | #[inline(always)] 96 | pub fn is_high(&self) -> bool { 97 | !self.is_low() 98 | } 99 | /// returns true if pin is at low level 100 | #[inline(always)] 101 | pub fn is_low(&self) -> bool { 102 | // NOTE(unsafe) atomic read with no side effects 103 | let gpio = unsafe { &(*gpiox::

()) }; 104 | gpio.idr.read().bits() & (1 << self.pin_number) != 0 105 | } 106 | } 107 | 108 | impl PartiallyErasedPin> { 109 | /// returns true if pin is at high level 110 | #[inline(always)] 111 | pub fn is_high(&self) -> bool { 112 | !self.is_low() 113 | } 114 | /// returns true if pin is at low level 115 | #[inline(always)] 116 | pub fn is_low(&self) -> bool { 117 | // NOTE(unsafe) atomic read with no side effects 118 | let gpio = unsafe { &(*gpiox::

()) }; 119 | gpio.idr.read().bits() & (1 << self.pin_number) != 0 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/i2c.rs: -------------------------------------------------------------------------------- 1 | //! API for the integrated I2C peripheral 2 | use core::ops::Deref; 3 | 4 | use embedded_hal_02::blocking::i2c::{Read, Write, WriteRead}; 5 | 6 | use crate::{ 7 | gpio::*, 8 | pac, 9 | rcc::{Clocks, Enable, Reset}, 10 | time::{kHz, Hertz, KiloHertz}, 11 | }; 12 | 13 | /// I2C abstraction 14 | pub struct I2c { 15 | i2c: I2C, 16 | pins: (SCLPIN, SDAPIN), 17 | } 18 | 19 | /// Trait for identifying SCL pins 20 | pub trait SclPin {} 21 | /// Trait for identifying SDA pins 22 | pub trait SdaPin {} 23 | 24 | macro_rules! i2c_pins { 25 | ($($I2C:ident => { 26 | scl => [$($scl:ty),+ $(,)*], 27 | sda => [$($sda:ty),+ $(,)*], 28 | })+) => { 29 | $( 30 | $( 31 | impl SclPin for $scl {} 32 | )+ 33 | $( 34 | impl SdaPin for $sda {} 35 | )+ 36 | )+ 37 | } 38 | } 39 | 40 | #[cfg(feature = "py32f030")] 41 | i2c_pins! { 42 | I2C => { 43 | scl => [ 44 | gpioa::PA3>, 45 | gpioa::PA8>, 46 | gpioa::PA9>, 47 | gpioa::PA10>, 48 | gpioa::PA11>, 49 | gpiob::PB6>, 50 | gpiob::PB8>, 51 | gpiof::PF1> 52 | ], 53 | sda => [ 54 | gpioa::PA2>, 55 | gpioa::PA7>, 56 | gpioa::PA9>, 57 | gpioa::PA10>, 58 | gpioa::PA12>, 59 | gpiob::PB7>, 60 | gpiof::PF0> 61 | ], 62 | } 63 | } 64 | 65 | #[cfg(feature = "py32f002a")] 66 | i2c_pins! { 67 | I2C => { 68 | scl => [ 69 | gpioa::PA3>, 70 | gpioa::PA8>, 71 | gpioa::PA9>, 72 | gpioa::PA10>, 73 | gpiob::PB6>, 74 | gpiob::PB8>, 75 | gpiof::PF1> 76 | ], 77 | sda => [ 78 | gpioa::PA2>, 79 | gpioa::PA7>, 80 | gpioa::PA9>, 81 | gpioa::PA10>, 82 | gpioa::PA12>, 83 | gpiob::PB7>, 84 | gpiof::PF0> 85 | ], 86 | } 87 | } 88 | 89 | #[cfg(feature = "py32f003")] 90 | i2c_pins! { 91 | I2C => { 92 | scl => [ 93 | gpioa::PA3>, 94 | gpiob::PB6>, 95 | gpiof::PF1> 96 | ], 97 | sda => [ 98 | gpioa::PA2>, 99 | gpioa::PA7>, 100 | gpioa::PA12>, 101 | gpiob::PB7>, 102 | gpiof::PF0> 103 | ], 104 | } 105 | } 106 | 107 | #[cfg(feature = "py32f002b")] 108 | i2c_pins! { 109 | I2C => { 110 | scl => [ 111 | gpioa::PA2>, 112 | gpiob::PB3>, 113 | ], 114 | sda => [ 115 | gpiob::PB4>, 116 | gpiob::PB6>, 117 | ], 118 | } 119 | } 120 | 121 | /// Error enum for I2C peripheral 122 | #[derive(Debug)] 123 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] 124 | pub enum Error { 125 | /// data has been overwritten before being read 126 | OVERRUN, 127 | /// An acknowledge error occurs when the interface detects a no acknowledge bit 128 | NACK, 129 | /// A bus error is generated when the I2C interface detects an 130 | /// external stop or start condition during an address or data 131 | /// byte transfer 132 | BUS, 133 | /// Packet error check error 134 | PEC, 135 | } 136 | 137 | // It's s needed for the impls, but rustc doesn't recognize that 138 | #[allow(dead_code)] 139 | type I2cRegisterBlock = crate::pac::i2c::RegisterBlock; 140 | 141 | /// trait for I2C peripheral instance 142 | pub trait Instance: Deref + crate::Sealed + Enable + Reset { 143 | #[doc(hidden)] 144 | fn ptr() -> *const I2cRegisterBlock; 145 | } 146 | 147 | macro_rules! i2c { 148 | ($($I2C:ident: $i2c:ident,)+) => { 149 | $( 150 | use crate::pac::$I2C; 151 | impl Instance for $I2C { 152 | fn ptr() -> *const I2cRegisterBlock { 153 | <$I2C>::ptr() 154 | } 155 | } 156 | 157 | impl I2c<$I2C, SCLPIN, SDAPIN> { 158 | /// Create an instance of I2C peripheral 159 | pub fn $i2c(i2c: $I2C, pins: (SCLPIN, SDAPIN), speed: KiloHertz, clocks: &Clocks) -> Self 160 | where 161 | SCLPIN: SclPin<$I2C>, 162 | SDAPIN: SdaPin<$I2C>, 163 | { 164 | let rcc = unsafe { &(*pac::RCC::ptr()) }; 165 | // Enable clock for I2C 166 | $I2C::enable(rcc); 167 | 168 | // Reset I2C 169 | $I2C::reset(rcc); 170 | I2c { i2c, pins }.i2c_init(clocks.pclk(), speed) 171 | } 172 | } 173 | )+ 174 | } 175 | } 176 | 177 | i2c! { 178 | I2C: i2c, 179 | } 180 | 181 | impl I2c 182 | where 183 | I2C: Instance, 184 | { 185 | fn i2c_init(self, freq: Hertz, speed: KiloHertz) -> Self { 186 | // Make sure the I2C unit is disabled so we can configure it 187 | self.i2c.cr1.modify(|_, w| w.pe().clear_bit()); 188 | 189 | let f = freq.raw() / 1_000_000; 190 | 191 | self.i2c 192 | .cr2 193 | .write(|w| unsafe { w.freq().bits(f.clamp(4, 48) as u8) }); 194 | 195 | // Normal I2C speeds use a different scaling than fast mode below 196 | let (f_s, ccr) = if speed <= kHz(100) { 197 | // This is a normal I2C mode 198 | (false, freq.raw() / (speed.raw() * 2)) 199 | } else { 200 | // This is a fast I2C mode 201 | ( 202 | true, 203 | if self.i2c.ccr.read().duty().bit_is_set() { 204 | freq.raw() / (speed.raw() * 25) 205 | } else { 206 | freq.raw() / (speed.raw() * 3) 207 | }, 208 | ) 209 | }; 210 | self.i2c 211 | .ccr 212 | .modify(|_, w| unsafe { w.f_s().bit(f_s).ccr().bits(ccr.clamp(4, 4095) as u16) }); 213 | 214 | // Enable the I2C processing 215 | self.i2c.cr1.modify(|_, w| w.pe().set_bit()); 216 | 217 | self 218 | } 219 | 220 | /// Release the I2C instance 221 | pub fn release(self) -> (I2C, (SCLPIN, SDAPIN)) { 222 | (self.i2c, self.pins) 223 | } 224 | 225 | fn check_and_clear_error_flags(&self, sr: &crate::pac::i2c::sr1::R) -> Result<(), Error> { 226 | // If we have a set pec error flag, clear it and return an PEC error 227 | if sr.pecerr().bit_is_set() { 228 | self.i2c.sr1.write(|w| w.pecerr().clear_bit()); 229 | return Err(Error::PEC); 230 | } 231 | 232 | // If we have a set overrun flag, clear it and return an OVERRUN error 233 | if sr.ovr().bit_is_set() { 234 | self.i2c.sr1.write(|w| w.ovr().clear_bit()); 235 | return Err(Error::OVERRUN); 236 | } 237 | 238 | // If we have a set arbitration error or bus error flag, clear it and return an BUS error 239 | if sr.arlo().bit_is_set() | sr.berr().bit_is_set() { 240 | self.i2c 241 | .sr1 242 | .write(|w| w.arlo().clear_bit().berr().clear_bit()); 243 | return Err(Error::BUS); 244 | } 245 | 246 | // If we received a NACK, then signal as a NACK error 247 | if sr.af().bit_is_set() { 248 | self.i2c.sr1.write(|w| w.af().clear_bit()); 249 | return Err(Error::NACK); 250 | } 251 | 252 | Ok(()) 253 | } 254 | 255 | fn send_byte(&self, byte: u8) -> Result<(), Error> { 256 | // Wait until we're ready for sending 257 | loop { 258 | let sr = self.i2c.sr1.read(); 259 | self.check_and_clear_error_flags(&sr)?; 260 | if sr.txe().bit_is_set() { 261 | break; 262 | } 263 | } 264 | 265 | // Push out a byte of data 266 | self.i2c.dr.write(|w| unsafe { w.bits(u32::from(byte)) }); 267 | 268 | self.check_and_clear_error_flags(&self.i2c.sr1.read())?; 269 | Ok(()) 270 | } 271 | 272 | fn recv_byte(&self) -> Result { 273 | loop { 274 | let sr = self.i2c.sr1.read(); 275 | self.check_and_clear_error_flags(&sr)?; 276 | if sr.rxne().bit_is_set() { 277 | break; 278 | } 279 | } 280 | 281 | let value = self.i2c.dr.read().bits() as u8; 282 | Ok(value) 283 | } 284 | } 285 | 286 | impl WriteRead for I2c 287 | where 288 | I2C: Instance, 289 | { 290 | type Error = Error; 291 | 292 | fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { 293 | // Set up current slave address for writing and disable autoending 294 | self.i2c.oar1.modify(|_, w| w.add().bits(addr)); 295 | 296 | // Send a START condition 297 | self.i2c.cr1.modify(|_, w| w.start().set_bit()); 298 | 299 | // Wait until the transmit buffer is empty and there hasn't been any error condition 300 | loop { 301 | let sr = self.i2c.sr1.read(); 302 | self.check_and_clear_error_flags(&sr)?; 303 | if sr.txe().bit_is_set() { 304 | break; 305 | } 306 | } 307 | 308 | // Send out all individual bytes 309 | for c in bytes { 310 | self.send_byte(*c)?; 311 | } 312 | 313 | // Wait until data was sent 314 | loop { 315 | let sr = self.i2c.sr1.read(); 316 | self.check_and_clear_error_flags(&sr)?; 317 | if sr.btf().bit_is_set() { 318 | break; 319 | } 320 | } 321 | 322 | // Set up current address for reading 323 | self.i2c.oar1.modify(|_, w| w.add().bits(addr)); 324 | 325 | // Send another START condition 326 | self.i2c.cr1.modify(|_, w| w.start().set_bit()); 327 | 328 | // Send the autoend after setting the start to get a restart 329 | // self.i2c.cr2.modify(|_, w| w.autoend().set_bit()); 330 | 331 | // Now read in all bytes 332 | for c in buffer.iter_mut() { 333 | *c = self.recv_byte()?; 334 | } 335 | 336 | // Check and clear flags if they somehow ended up set 337 | self.check_and_clear_error_flags(&self.i2c.sr1.read())?; 338 | 339 | Ok(()) 340 | } 341 | } 342 | 343 | impl Read for I2c 344 | where 345 | I2C: Instance, 346 | { 347 | type Error = Error; 348 | 349 | fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { 350 | // Set up current address for reading 351 | self.i2c.oar1.modify(|_, w| w.add().bits(addr)); 352 | 353 | // Send a START condition 354 | self.i2c.cr1.modify(|_, w| w.start().set_bit()); 355 | 356 | // Send the autoend after setting the start to get a restart 357 | // self.i2c.cr2.modify(|_, w| w.autoend().set_bit()); 358 | 359 | // Now read in all bytes 360 | for c in buffer.iter_mut() { 361 | *c = self.recv_byte()?; 362 | } 363 | 364 | // Check and clear flags if they somehow ended up set 365 | self.check_and_clear_error_flags(&self.i2c.sr1.read())?; 366 | 367 | Ok(()) 368 | } 369 | } 370 | 371 | impl Write for I2c 372 | where 373 | I2C: Instance, 374 | { 375 | type Error = Error; 376 | 377 | fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { 378 | // Set up current slave address for writing and enable autoending 379 | self.i2c.oar1.modify(|_, w| w.add().bits(addr)); 380 | 381 | // Send a START condition 382 | self.i2c.cr1.modify(|_, w| w.start().set_bit()); 383 | 384 | // Send out all individual bytes 385 | for c in bytes { 386 | self.send_byte(*c)?; 387 | } 388 | 389 | // Check and clear flags if they somehow ended up set 390 | self.check_and_clear_error_flags(&self.i2c.sr1.read())?; 391 | 392 | Ok(()) 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # HAL for the py32f0xx family of microcontrollers 2 | //! 3 | //! This is an implementation of the [`embedded-hal`] traits for the py32f0xx family of 4 | //! microcontrollers. 5 | //! 6 | //! [`embedded-hal`]: https://crates.io/crates/embedded-hal 7 | //! 8 | //! ## Usage 9 | //! 10 | //! This crate supports multiple microcontrollers in the 11 | //! py32f0xx family. Which specific microcontroller you want to build for has to be 12 | //! specified with a feature, for example `py32f030`. 13 | //! 14 | //! If no microcontroller is specified, the crate will not compile. 15 | //! 16 | //! The currently supported variants are 17 | //! 18 | //! - `py32f002a` 19 | //! - `py32f002b` 20 | //! - `py32f003` 21 | //! - `py32f030` 22 | //! 23 | //! ## Commonly used setup 24 | //! Almost all peripherals require references to some registers in `RCC`. The following 25 | //! code shows how to set up those registers 26 | //! 27 | //! ```rust 28 | //! // Get access to the device specific peripherals from the peripheral access crate 29 | //! let mut dp = pac::Peripherals::take().unwrap(); 30 | //! 31 | //! // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 32 | //! // `clocks` 33 | //! let clocks = dp.RCC.configure.freeze(&mut dp.FLASH); 34 | //! ``` 35 | //! 36 | //! ## Usage examples 37 | //! 38 | //! See the [examples] folder. 39 | //! 40 | //! Most of the examples require the following additional dependencies 41 | //! ```toml 42 | //! [dependencies] 43 | //! cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 44 | //! cortex-m-rt = "0.7.3" 45 | //! # Panic behaviour, see https://crates.io/keywords/panic-impl for alternatives 46 | //! panic-halt = "0.2.0" 47 | //! ``` 48 | //! 49 | //! [examples]: https://github.com/py32-rust/py32f0xx-hal/tree/main/examples 50 | //! [README]: https://github.com/py32-rust/py32f0xx-hal/tree/main 51 | #![no_std] 52 | #![allow(non_camel_case_types)] 53 | #![allow(clippy::uninit_assumed_init)] 54 | #![deny(missing_docs)] 55 | 56 | #[cfg(feature = "py32f002a")] 57 | pub use py32f0::py32f002a as pac; 58 | #[cfg(feature = "py32f002b")] 59 | pub use py32f0::py32f002b as pac; 60 | #[cfg(feature = "py32f003")] 61 | pub use py32f0::py32f003 as pac; 62 | #[cfg(feature = "py32f030")] 63 | pub use py32f0::py32f030 as pac; 64 | 65 | #[cfg(feature = "device-selected")] 66 | pub mod adc; 67 | #[cfg(all(feature = "device-selected", feature = "with-dma"))] 68 | pub mod dma; 69 | #[cfg(feature = "device-selected")] 70 | pub mod gpio; 71 | #[cfg(feature = "device-selected")] 72 | pub mod i2c; 73 | #[cfg(feature = "device-selected")] 74 | pub mod prelude; 75 | #[cfg(feature = "device-selected")] 76 | pub mod rcc; 77 | #[cfg(any(feature = "py32f003", feature = "py32f030"))] 78 | pub mod rtc; 79 | #[cfg(feature = "device-selected")] 80 | pub mod serial; 81 | #[cfg(feature = "device-selected")] 82 | pub mod spi; 83 | #[cfg(feature = "device-selected")] 84 | pub mod time; 85 | #[cfg(feature = "device-selected")] 86 | pub mod timer; 87 | #[cfg(feature = "device-selected")] 88 | pub mod watchdog; 89 | 90 | mod sealed { 91 | pub trait Sealed {} 92 | } 93 | use sealed::Sealed; 94 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! Collection of traits that should be in scope when using this crate 2 | #[cfg(feature = "with-dma")] 3 | pub use crate::dma::CircReadDma as _py32f0xx_hal_dma_CircReadDma; 4 | #[cfg(feature = "with-dma")] 5 | pub use crate::dma::DmaExt as _py32f0xx_hal_dma_DmaExt; 6 | #[cfg(feature = "with-dma")] 7 | pub use crate::dma::ReadDma as _py32f0xx_hal_dma_ReadDma; 8 | #[cfg(feature = "with-dma")] 9 | pub use crate::dma::ReadWriteDma as _py32f0xx_hal_dma_ReadWriteDma; 10 | #[cfg(feature = "with-dma")] 11 | pub use crate::dma::WriteDma as _py32f0xx_hal_dma_WriteDma; 12 | pub use crate::gpio::GpioExt as _py32f0xx_hal_gpio_GpioExt; 13 | pub use crate::rcc::RccExt as _py32f0xx_hal_rcc_RccExt; 14 | pub use crate::serial::SerialExt as _py32f0xx_hal_serial_SerialExt; 15 | pub use crate::spi::SpiExt as _py32f0xx_hal_spi_SpiExt; 16 | pub use crate::time::U32Ext as _py32f0xx_hal_time_U32Ext; 17 | #[cfg(feature = "rtic")] 18 | pub use crate::timer::monotonic::MonoTimerExt as _py32f0xx_hal_timer_monotonic_MonoTimerExt; 19 | pub use crate::timer::pwm::PwmExt as _py32f0xx_hal_timer_pwm_PwmExt; 20 | pub use crate::timer::SysTimerExt as _py32f0xx_hal_timer_SysTimerExt; 21 | pub use crate::timer::TimerExt as _py32f0xx_hal_timer_TimerExt; 22 | pub use fugit::ExtU32 as _fugit_ExtU32; 23 | pub use fugit::RateExtU32 as _fugit_RateExtU32; 24 | -------------------------------------------------------------------------------- /src/rcc/enable.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | macro_rules! bus { 4 | ($($PER:ident => ($apbX:ty, $enr:tt, $rstr:tt, $bit:literal),)+) => { 5 | $( 6 | impl crate::Sealed for crate::pac::$PER {} 7 | 8 | impl RccBus for crate::pac::$PER { 9 | type Bus = $apbX; 10 | } 11 | impl Enable for crate::pac::$PER { 12 | #[inline(always)] 13 | fn enable(rcc: &rcc::RegisterBlock) { 14 | unsafe { 15 | rcc.$enr.modify(|r,w| w.bits(r.bits() | (1 << $bit))); 16 | } 17 | } 18 | #[inline(always)] 19 | fn disable(rcc: &rcc::RegisterBlock) { 20 | unsafe { 21 | rcc.$enr.modify(|r,w| w.bits(r.bits() & !(1 << $bit))); 22 | } 23 | } 24 | } 25 | impl Reset for crate::pac::$PER { 26 | #[inline(always)] 27 | fn reset(rcc: &rcc::RegisterBlock) { 28 | unsafe { 29 | rcc.$rstr.modify(|r,w| w.bits(r.bits() | (1 << $bit))); 30 | rcc.$rstr.modify(|r,w| w.bits(r.bits() & !(1 << $bit))); 31 | } 32 | } 33 | } 34 | )+ 35 | } 36 | } 37 | 38 | bus! { 39 | ADC => (APB, apbenr2, apbrstr2, 20), 40 | CRC => (AHB, ahbenr, ahbrstr, 12), 41 | DBG => (APB, apbenr1, apbrstr1, 27), 42 | GPIOA => (APB, iopenr, ioprstr, 0), 43 | GPIOB => (APB, iopenr, ioprstr, 1), 44 | I2C => (APB, apbenr1, apbrstr1, 21), 45 | PWR => (APB, apbenr1, apbrstr1, 28), 46 | SPI1 => (APB, apbenr2, apbrstr2, 12), 47 | SYSCFG => (APB, apbenr2, apbrstr2, 0), 48 | TIM1 => (APB, apbenr2, apbrstr2, 11), 49 | USART1 => (APB, apbenr2, apbrstr2, 14), 50 | } 51 | 52 | #[cfg(any(feature = "py32f003", feature = "py32f030"))] 53 | bus! { 54 | USART2 => (APB, apbenr1, apbrstr1, 17), 55 | DMA => (AHB, ahbenr, ahbrstr, 0), 56 | WWDG => (APB, apbenr1, apbrstr1, 11), 57 | } 58 | 59 | #[cfg(any(feature = "py32f030"))] 60 | bus! { 61 | SPI2 => (APB, apbenr1, apbrstr1, 14), 62 | } 63 | 64 | #[cfg(any(feature = "py32f002a", feature = "py32f003", feature = "py32f030"))] 65 | bus! { 66 | GPIOF => (APB, iopenr, ioprstr, 5), 67 | LPTIM => (APB, apbenr1, apbrstr1, 31), 68 | } 69 | 70 | #[cfg(any(feature = "py32f002b"))] 71 | bus! { 72 | GPIOC => (APB, iopenr, ioprstr, 2), 73 | LPTIM1 => (APB, apbenr1, apbrstr1, 31), 74 | } 75 | 76 | #[cfg(any(feature = "py32f030"))] 77 | bus! { 78 | LED => (APB, apbenr2, apbrstr2, 23), 79 | } 80 | 81 | #[cfg(any(feature = "py32f003", feature = "py32f030"))] 82 | bus! { 83 | TIM3 => (APB, apbenr1, apbrstr1, 1), 84 | TIM17 => (APB, apbenr2, apbrstr2, 18), 85 | } 86 | 87 | #[cfg(any(feature = "py32f002b", feature = "py32f003", feature = "py32f030"))] 88 | bus! { 89 | TIM14 => (APB, apbenr2, apbrstr2, 15), 90 | } 91 | 92 | #[cfg(any(feature = "py32f002a", feature = "py32f003", feature = "py32f030"))] 93 | bus! { 94 | TIM16 => (APB, apbenr2, apbrstr2, 17), 95 | } 96 | -------------------------------------------------------------------------------- /src/rtc.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Real time clock 3 | */ 4 | use crate::pac::{RCC, RTC}; 5 | 6 | use crate::time::{Hertz, Hz}; 7 | 8 | use core::convert::Infallible; 9 | use core::marker::PhantomData; 10 | 11 | // The LSE runs at at 32 768 hertz unless an external clock is provided 12 | #[cfg(feature = "py32f030")] 13 | const LSE_HERTZ: Hertz = Hz(32_768); 14 | const LSI_HERTZ: Hertz = Hz(32_768); 15 | 16 | /// RTC clock source HSE clock divided by 128 (type state) 17 | pub struct RtcClkHseDiv128; 18 | /// RTC clock source LSE oscillator clock (type state) 19 | #[cfg(feature = "py32f030")] 20 | pub struct RtcClkLse; 21 | /// RTC clock source LSI oscillator clock (type state) 22 | pub struct RtcClkLsi; 23 | 24 | /// Enum to track the state for RTC re-initialization 25 | pub enum RestoredOrNewRtc { 26 | /// Restored 27 | Restored(Rtc), 28 | /// New 29 | New(Rtc), 30 | } 31 | 32 | /** 33 | Real time clock 34 | 35 | A continuously running clock that counts seconds¹. It can optionally 36 | be enabled during Sleep or Stop mode so that the counter is not affected by 37 | these modes.This allows it to be used to wake the 38 | CPU when it is in low power mode. 39 | 40 | 41 | See [examples/rtc.rs] and [examples/blinky_rtc.rs] for usage examples. 42 | 43 | 1: Unless configured to another frequency using [select_frequency](struct.Rtc.html#method.select_frequency) 44 | 45 | [examples/rtc.rs]: https://github.com/py32f0xx-hal/blob/v0.2.0/examples/rtc.rs 46 | [examples/blinky_rtc.rs]: https://github.com/py32f0xx-hal/blob/v0.2.0/examples/blinky_rtc.rs 47 | */ 48 | 49 | pub struct Rtc { 50 | regs: RTC, 51 | _clock_source: PhantomData, 52 | } 53 | 54 | #[cfg(feature = "py32f030")] 55 | impl Rtc { 56 | /** 57 | Initialises the RTC with low-speed external crystal source (lse). 58 | 59 | The frequency is set to 1 Hz. 60 | 61 | In case application is running off a battery on VBAT, 62 | this method will reset the RTC every time, leading to lost time, 63 | you may want to use 64 | [`restore_or_new`](Rtc::::restore_or_new) instead. 65 | */ 66 | pub fn new(regs: RTC) -> Self { 67 | let mut result = Rtc { 68 | regs, 69 | _clock_source: PhantomData, 70 | }; 71 | 72 | Self::enable_rtc(); 73 | 74 | // Set the prescaler to make it count up once every second. 75 | let prl = LSE_HERTZ.raw() - 1; 76 | assert!(prl < 1 << 20); 77 | result.perform_write(|s| { 78 | s.regs.prlh.write(|w| unsafe { w.bits(prl >> 16) }); 79 | s.regs.prll.write(|w| unsafe { w.bits(prl as u16 as u32) }); 80 | }); 81 | 82 | result 83 | } 84 | 85 | /// Tries to obtain currently running RTC to prevent a reset in case it was running from VBAT. 86 | /// If the RTC is not running, or is not LSE, it will be reinitialized. 87 | /// 88 | /// # Examples 89 | /// ``` 90 | /// let rtc = match Rtc::restore_or_new(p.RTC, &mut backup_domain) { 91 | /// Restored(rtc) => rtc, // The rtc is restored from previous configuration. You may verify the frequency you want if needed. 92 | /// New(rtc) => { // The rtc was just initialized, the clock source selected, frequency is 1.Hz() 93 | /// // Initialize rtc with desired parameters 94 | /// rtc.select_frequency(2u16.Hz()); // Set the frequency to 2 Hz. This will stay same after reset 95 | /// rtc 96 | /// } 97 | /// }; 98 | /// ``` 99 | pub fn restore_or_new(regs: RTC) -> RestoredOrNewRtc { 100 | if !Self::is_enabled() { 101 | RestoredOrNewRtc::New(Rtc::new(regs)) 102 | } else { 103 | RestoredOrNewRtc::Restored(Rtc { 104 | regs, 105 | _clock_source: PhantomData, 106 | }) 107 | } 108 | } 109 | 110 | /// Returns whether the RTC is currently enabled and LSE is selected. 111 | fn is_enabled() -> bool { 112 | let rcc = unsafe { &*RCC::ptr() }; 113 | let bdcr = rcc.bdcr.read(); 114 | bdcr.rtcen().is_enabled() && bdcr.rtcsel().is_lse() 115 | } 116 | 117 | /// Enables the RTC device with the lse as the clock 118 | fn enable_rtc() { 119 | // NOTE: Safe RCC access because we are only accessing bdcr 120 | // and we have a &mut on BackupDomain 121 | let rcc = unsafe { &*RCC::ptr() }; 122 | rcc.bdcr.modify(|_, w| { 123 | // start the LSE oscillator 124 | w.lseon().set_bit(); 125 | // Enable the RTC 126 | w.rtcen().set_bit(); 127 | // Set the source of the RTC to LSE 128 | w.rtcsel().lse() 129 | }) 130 | } 131 | } 132 | 133 | impl Rtc { 134 | /** 135 | Initialises the RTC with low-speed internal oscillator source (lsi). 136 | 137 | The frequency is set to 1 Hz. 138 | 139 | In case application is running of a battery on VBAT, 140 | this method will reset the RTC every time, leading to lost time, 141 | you may want to use 142 | [`restore_or_new_lsi`](Rtc::::restore_or_new_lsi) instead. 143 | */ 144 | pub fn new_lsi(regs: RTC) -> Self { 145 | let mut result = Rtc { 146 | regs, 147 | _clock_source: PhantomData, 148 | }; 149 | 150 | Self::enable_rtc(); 151 | 152 | // Set the prescaler to make it count up once every second. 153 | let prl = LSI_HERTZ.raw() - 1; 154 | assert!(prl < 1 << 20); 155 | result.perform_write(|s| { 156 | s.regs.prlh.write(|w| unsafe { w.bits(prl >> 16) }); 157 | s.regs.prll.write(|w| unsafe { w.bits(prl as u16 as u32) }); 158 | }); 159 | 160 | result 161 | } 162 | 163 | /// Tries to obtain currently running RTC to prevent reset in case it was running from VBAT. 164 | /// If the RTC is not running, or is not LSI, it will be reinitialized. 165 | pub fn restore_or_new_lsi(regs: RTC) -> RestoredOrNewRtc { 166 | if !Rtc::::is_enabled() { 167 | RestoredOrNewRtc::New(Rtc::new_lsi(regs)) 168 | } else { 169 | RestoredOrNewRtc::Restored(Rtc { 170 | regs, 171 | _clock_source: PhantomData, 172 | }) 173 | } 174 | } 175 | 176 | /// Returns whether the RTC is currently enabled and LSI is selected. 177 | fn is_enabled() -> bool { 178 | let rcc = unsafe { &*RCC::ptr() }; 179 | rcc.bdcr.read().rtcen().bit() && rcc.bdcr.read().rtcsel().is_lsi() 180 | } 181 | 182 | /// Enables the RTC device with the lsi as the clock 183 | fn enable_rtc() { 184 | // NOTE: Safe RCC access because we are only accessing bdcr 185 | // and we have a &mut on BackupDomain 186 | let rcc = unsafe { &*RCC::ptr() }; 187 | rcc.csr.modify(|_, w| { 188 | // start the LSI oscillator 189 | w.lsion().set_bit() 190 | }); 191 | rcc.bdcr.modify(|_, w| { 192 | // Enable the RTC 193 | w.rtcen().set_bit(); 194 | // Set the source of the RTC to LSI 195 | w.rtcsel().lsi() 196 | }) 197 | } 198 | } 199 | 200 | impl Rtc { 201 | /** 202 | Initialises the RTC with high-speed external oscillator source (hse) 203 | divided by 128. 204 | 205 | The frequency is set to 1 Hz. 206 | 207 | In case application is running of a battery on VBAT, 208 | this method will reset the RTC every time, leading to lost time, 209 | you may want to use 210 | [`restore_or_new_hse`](Rtc::::restore_or_new_hse) instead. 211 | */ 212 | pub fn new_hse(regs: RTC, hse: Hertz) -> Self { 213 | let mut result = Rtc { 214 | regs, 215 | _clock_source: PhantomData, 216 | }; 217 | 218 | Self::enable_rtc(); 219 | 220 | // Set the prescaler to make it count up once every second. 221 | let prl = hse.raw() / 128 - 1; 222 | assert!(prl < 1 << 20); 223 | result.perform_write(|s| { 224 | s.regs.prlh.write(|w| unsafe { w.bits(prl >> 16) }); 225 | s.regs.prll.write(|w| unsafe { w.bits(prl as u16 as u32) }); 226 | }); 227 | 228 | result 229 | } 230 | 231 | /// Tries to obtain currently running RTC to prevent reset in case it was running from VBAT. 232 | /// If the RTC is not running, or is not HSE, it will be reinitialized. 233 | pub fn restore_or_new_hse(regs: RTC, hse: Hertz) -> RestoredOrNewRtc { 234 | if !Self::is_enabled() { 235 | RestoredOrNewRtc::New(Rtc::new_hse(regs, hse)) 236 | } else { 237 | RestoredOrNewRtc::Restored(Rtc { 238 | regs, 239 | _clock_source: PhantomData, 240 | }) 241 | } 242 | } 243 | 244 | fn is_enabled() -> bool { 245 | let rcc = unsafe { &*RCC::ptr() }; 246 | let bdcr = rcc.bdcr.read(); 247 | bdcr.rtcen().is_enabled() && bdcr.rtcsel().is_hse() 248 | } 249 | 250 | /// Enables the RTC device with the lsi as the clock 251 | fn enable_rtc() { 252 | // NOTE: Safe RCC access because we are only accessing bdcr 253 | // and we have a &mut on BackupDomain 254 | let rcc = unsafe { &*RCC::ptr() }; 255 | if rcc.cr.read().hserdy().bit_is_clear() { 256 | panic!("HSE oscillator not ready"); 257 | } 258 | rcc.bdcr.modify(|_, w| { 259 | // Enable the RTC 260 | w.rtcen().set_bit(); 261 | // Set the source of the RTC to HSE/128 262 | w.rtcsel().hse() 263 | }) 264 | } 265 | } 266 | 267 | impl Rtc { 268 | /// Selects the frequency of the RTC Timer 269 | /// NOTE: Maximum frequency of 16384 Hz using the internal LSI 270 | pub fn select_frequency(&mut self, frequency: Hertz) { 271 | // The manual says that the zero value for the prescaler is not recommended, thus the 272 | // minimum division factor is 2 (prescaler + 1) 273 | assert!(frequency <= LSI_HERTZ / 2); 274 | 275 | let prescaler = LSI_HERTZ.raw() / frequency.raw() - 1; 276 | self.perform_write(|s| { 277 | s.regs.prlh.write(|w| unsafe { w.bits(prescaler >> 16) }); 278 | s.regs 279 | .prll 280 | .write(|w| unsafe { w.bits(prescaler as u16 as u32) }); 281 | }); 282 | } 283 | 284 | /// Set the current RTC counter value to the specified amount 285 | pub fn set_time(&mut self, counter_value: u32) { 286 | self.perform_write(|s| { 287 | s.regs 288 | .cnth 289 | .write(|w| unsafe { w.bits(counter_value >> 16) }); 290 | s.regs 291 | .cntl 292 | .write(|w| unsafe { w.bits(counter_value as u16 as u32) }); 293 | }); 294 | } 295 | 296 | /** 297 | Sets the time at which an alarm will be triggered 298 | 299 | This also clears the alarm flag if it is set 300 | */ 301 | pub fn set_alarm(&mut self, counter_value: u32) { 302 | // Set alarm time 303 | // See section 18.3.5 for explanation 304 | let alarm_value = counter_value - 1; 305 | 306 | self.perform_write(|s| { 307 | s.regs 308 | .alrh 309 | .write(|w| w.alrh().bits((alarm_value >> 16) as u16)); 310 | s.regs.alrl.write(|w| w.alrl().bits(alarm_value as u16)); 311 | }); 312 | 313 | self.clear_alarm_flag(); 314 | } 315 | 316 | /// Enables the RTC interrupt to trigger when the counter reaches the alarm value. In addition, 317 | /// if the EXTI controller has been set up correctly, this function also enables the RTCALARM 318 | /// interrupt. 319 | pub fn listen_alarm(&mut self) { 320 | // Enable alarm interrupt 321 | self.perform_write(|s| { 322 | s.regs.crh.modify(|_, w| w.alrie().set_bit()); 323 | }) 324 | } 325 | 326 | /// Stops the RTC alarm from triggering the RTC and RTCALARM interrupts 327 | pub fn unlisten_alarm(&mut self) { 328 | // Disable alarm interrupt 329 | self.perform_write(|s| { 330 | s.regs.crh.modify(|_, w| w.alrie().clear_bit()); 331 | }) 332 | } 333 | 334 | /// Reads the current counter 335 | pub fn current_time(&self) -> u32 { 336 | // Wait for the APB1 interface to be ready 337 | while !self.regs.crl.read().rsf().bit() {} 338 | 339 | self.regs.cnth.read().bits() << 16 | self.regs.cntl.read().bits() 340 | } 341 | 342 | /// Enables triggering the RTC interrupt every time the RTC counter is increased 343 | pub fn listen_seconds(&mut self) { 344 | self.perform_write(|s| s.regs.crh.modify(|_, w| w.secie().set_bit())) 345 | } 346 | 347 | /// Disables the RTC second interrupt 348 | pub fn unlisten_seconds(&mut self) { 349 | self.perform_write(|s| s.regs.crh.modify(|_, w| w.secie().clear_bit())) 350 | } 351 | 352 | /// Clears the RTC second interrupt flag 353 | pub fn clear_second_flag(&mut self) { 354 | self.perform_write(|s| s.regs.crl.modify(|_, w| w.secf().clear_bit())) 355 | } 356 | 357 | /// Clears the RTC alarm interrupt flag 358 | pub fn clear_alarm_flag(&mut self) { 359 | self.perform_write(|s| s.regs.crl.modify(|_, w| w.alrf().clear_bit())) 360 | } 361 | 362 | /** 363 | Return `Ok(())` if the alarm flag is set, `Err(nb::WouldBlock)` otherwise. 364 | 365 | ```rust 366 | use nb::block; 367 | 368 | rtc.set_alarm(rtc.read_counts() + 5); 369 | // NOTE: Safe unwrap because Infallible can't be returned 370 | block!(rtc.wait_alarm()).unwrap(); 371 | ``` 372 | */ 373 | pub fn wait_alarm(&mut self) -> nb::Result<(), Infallible> { 374 | if self.regs.crl.read().alrf().bit() { 375 | self.regs.crl.modify(|_, w| w.alrf().clear_bit()); 376 | Ok(()) 377 | } else { 378 | Err(nb::Error::WouldBlock) 379 | } 380 | } 381 | 382 | /** 383 | The RTC registers can not be written to at any time as documented on page 384 | 350 of the manual. Performing writes using this function ensures that 385 | the writes are done correctly. 386 | */ 387 | fn perform_write(&mut self, func: impl Fn(&mut Self)) { 388 | // Wait for the last write operation to be done 389 | while !self.regs.crl.read().rtoff().bit() {} 390 | // Put the clock into config mode 391 | self.regs.crl.modify(|_, w| w.cnf().set_bit()); 392 | 393 | // Perform the write operation 394 | func(self); 395 | 396 | // Take the device out of config mode 397 | self.regs.crl.modify(|_, w| w.cnf().clear_bit()); 398 | // Wait for the write to be done 399 | while !self.regs.crl.read().rtoff().bit() {} 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /src/serial/hal_02.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use embedded_hal_02::{blocking::serial as blocking, serial}; 3 | 4 | impl serial::Write for Tx { 5 | type Error = Error; 6 | 7 | fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { 8 | self.write_u8(word) 9 | } 10 | 11 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 12 | self.flush() 13 | } 14 | } 15 | 16 | impl serial::Write for Tx { 17 | type Error = Error; 18 | 19 | fn write(&mut self, word: u16) -> nb::Result<(), Self::Error> { 20 | self.write_u16(word) 21 | } 22 | 23 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 24 | self.flush() 25 | } 26 | } 27 | 28 | impl serial::Read for Rx { 29 | type Error = Error; 30 | 31 | fn read(&mut self) -> nb::Result { 32 | self.read() 33 | } 34 | } 35 | 36 | impl serial::Read for Rx { 37 | type Error = Error; 38 | 39 | fn read(&mut self) -> nb::Result { 40 | self.read_u16() 41 | } 42 | } 43 | 44 | impl serial::Write for Serial { 45 | type Error = Error; 46 | 47 | fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { 48 | self.tx.write_u8(word) 49 | } 50 | 51 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 52 | self.tx.flush() 53 | } 54 | } 55 | 56 | impl serial::Write for Serial { 57 | type Error = Error; 58 | 59 | fn write(&mut self, word: u16) -> nb::Result<(), Self::Error> { 60 | self.tx.write_u16(word) 61 | } 62 | 63 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 64 | self.tx.flush() 65 | } 66 | } 67 | 68 | impl serial::Read for Serial { 69 | type Error = Error; 70 | 71 | fn read(&mut self) -> nb::Result { 72 | self.rx.read() 73 | } 74 | } 75 | 76 | impl serial::Read for Serial { 77 | type Error = Error; 78 | 79 | fn read(&mut self) -> nb::Result { 80 | self.rx.read_u16() 81 | } 82 | } 83 | 84 | // Blocking 85 | 86 | impl blocking::Write for Tx { 87 | type Error = Error; 88 | 89 | fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { 90 | self.bwrite_all_u8(buffer) 91 | } 92 | 93 | fn bflush(&mut self) -> Result<(), Self::Error> { 94 | self.bflush() 95 | } 96 | } 97 | 98 | impl blocking::Write for Tx { 99 | type Error = Error; 100 | 101 | fn bwrite_all(&mut self, buffer: &[u16]) -> Result<(), Self::Error> { 102 | self.bwrite_all_u16(buffer) 103 | } 104 | 105 | fn bflush(&mut self) -> Result<(), Self::Error> { 106 | self.bflush() 107 | } 108 | } 109 | 110 | impl blocking::Write for Serial { 111 | type Error = Error; 112 | 113 | fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { 114 | self.tx.bwrite_all_u8(buffer) 115 | } 116 | 117 | fn bflush(&mut self) -> Result<(), Self::Error> { 118 | self.tx.bflush() 119 | } 120 | } 121 | 122 | impl blocking::Write for Serial { 123 | type Error = Error; 124 | 125 | fn bwrite_all(&mut self, buffer: &[u16]) -> Result<(), Self::Error> { 126 | self.tx.bwrite_all_u16(buffer) 127 | } 128 | 129 | fn bflush(&mut self) -> Result<(), Self::Error> { 130 | self.tx.bflush() 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/serial/hal_1.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | mod nb { 4 | use super::{Error, Instance, Rx, Serial, Tx}; 5 | use embedded_hal_nb::serial::ErrorKind; 6 | use embedded_hal_nb::{serial, serial::ErrorType}; 7 | 8 | impl embedded_hal_nb::serial::Error for Error { 9 | fn kind(&self) -> ErrorKind { 10 | match self { 11 | Error::Overrun => ErrorKind::Overrun, 12 | Error::FrameFormat => ErrorKind::FrameFormat, 13 | Error::Parity => ErrorKind::Parity, 14 | Error::Noise => ErrorKind::Noise, 15 | Error::Other => ErrorKind::Other, 16 | } 17 | } 18 | } 19 | 20 | impl ErrorType for Tx { 21 | type Error = Error; 22 | } 23 | 24 | impl ErrorType for Rx { 25 | type Error = Error; 26 | } 27 | 28 | impl ErrorType for Serial { 29 | type Error = Error; 30 | } 31 | 32 | impl serial::Write for Tx { 33 | fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { 34 | self.write_u8(word)?; 35 | Ok(()) 36 | } 37 | 38 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 39 | self.flush() 40 | } 41 | } 42 | 43 | impl serial::Write for Tx { 44 | fn write(&mut self, word: u16) -> nb::Result<(), Self::Error> { 45 | self.write_u16(word) 46 | } 47 | 48 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 49 | self.flush() 50 | } 51 | } 52 | 53 | impl serial::Read for Rx { 54 | fn read(&mut self) -> nb::Result { 55 | self.read() 56 | } 57 | } 58 | 59 | impl serial::Read for Rx { 60 | fn read(&mut self) -> nb::Result { 61 | self.read_u16() 62 | } 63 | } 64 | 65 | impl serial::Write for Serial { 66 | fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { 67 | self.tx.write_u8(word).unwrap(); 68 | Ok(()) 69 | } 70 | 71 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 72 | self.tx.flush().unwrap(); 73 | Ok(()) 74 | } 75 | } 76 | 77 | impl serial::Write for Serial { 78 | fn write(&mut self, word: u16) -> nb::Result<(), Self::Error> { 79 | self.tx.write_u16(word).unwrap(); 80 | Ok(()) 81 | } 82 | 83 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 84 | self.tx.flush().unwrap(); 85 | Ok(()) 86 | } 87 | } 88 | 89 | impl serial::Read for Serial { 90 | fn read(&mut self) -> nb::Result { 91 | self.rx.read() 92 | } 93 | } 94 | 95 | impl serial::Read for Serial { 96 | fn read(&mut self) -> nb::Result { 97 | self.rx.read_u16() 98 | } 99 | } 100 | } 101 | 102 | mod io { 103 | use super::super::{Error, Instance, Rx, Serial, Tx}; 104 | use embedded_io::Write; 105 | 106 | impl embedded_io::Error for Error { 107 | fn kind(&self) -> embedded_io::ErrorKind { 108 | embedded_io::ErrorKind::Other 109 | } 110 | } 111 | 112 | impl embedded_io::ErrorType for Serial { 113 | type Error = Error; 114 | } 115 | 116 | impl embedded_io::ErrorType for Tx { 117 | type Error = Error; 118 | } 119 | 120 | impl embedded_io::ErrorType for Rx { 121 | type Error = Error; 122 | } 123 | 124 | impl Write for Tx { 125 | fn write(&mut self, bytes: &[u8]) -> Result { 126 | let mut i = 0; 127 | for byte in bytes.iter() { 128 | match self.write_u8(*byte) { 129 | Ok(_) => { 130 | i += 1; 131 | } 132 | Err(nb::Error::WouldBlock) => { 133 | return Ok(i); 134 | } 135 | Err(nb::Error::Other(e)) => { 136 | return Err(e); 137 | } 138 | } 139 | } 140 | Ok(i) 141 | } 142 | 143 | fn flush(&mut self) -> Result<(), Self::Error> { 144 | self.bflush()?; 145 | Ok(()) 146 | } 147 | } 148 | 149 | impl Write for Serial 150 | where 151 | Tx: Write, 152 | { 153 | fn write(&mut self, bytes: &[u8]) -> Result { 154 | self.tx.write(bytes) 155 | } 156 | 157 | fn flush(&mut self) -> Result<(), Self::Error> { 158 | Write::flush(&mut self.tx) 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/spi/hal_02.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub use embedded_hal_02::spi::{Mode, Phase, Polarity}; 4 | use embedded_hal_02::{blocking::spi as blocking, spi}; 5 | 6 | impl From for super::Polarity { 7 | fn from(p: Polarity) -> Self { 8 | match p { 9 | Polarity::IdleLow => Self::IdleLow, 10 | Polarity::IdleHigh => Self::IdleHigh, 11 | } 12 | } 13 | } 14 | 15 | impl From for super::Phase { 16 | fn from(p: Phase) -> Self { 17 | match p { 18 | Phase::CaptureOnFirstTransition => Self::CaptureOnFirstTransition, 19 | Phase::CaptureOnSecondTransition => Self::CaptureOnSecondTransition, 20 | } 21 | } 22 | } 23 | 24 | impl From for super::Mode { 25 | fn from(m: Mode) -> Self { 26 | Self { 27 | polarity: m.polarity.into(), 28 | phase: m.phase.into(), 29 | } 30 | } 31 | } 32 | 33 | impl spi::FullDuplex for Spi 34 | where 35 | SPI: Instance, 36 | W: Copy, 37 | { 38 | type Error = Error; 39 | 40 | fn read(&mut self) -> nb::Result { 41 | self.read_nonblocking() 42 | } 43 | 44 | fn send(&mut self, data: W) -> nb::Result<(), Error> { 45 | self.write_nonblocking(data) 46 | } 47 | } 48 | 49 | impl blocking::transfer::Default 50 | for Spi 51 | where 52 | SPI: Instance, 53 | W: Copy, 54 | { 55 | } 56 | 57 | impl blocking::Write 58 | for Spi 59 | { 60 | type Error = Error; 61 | 62 | fn write(&mut self, words: &[u8]) -> Result<(), Error> { 63 | self.deref_mut().write(words) 64 | } 65 | } 66 | 67 | impl blocking::Write 68 | for Spi 69 | { 70 | type Error = Error; 71 | 72 | fn write(&mut self, words: &[u16]) -> Result<(), Error> { 73 | self.deref_mut().write(words) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/spi/hal_1.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | pub use embedded_hal::spi::{ErrorKind, ErrorType, Mode, Phase, Polarity}; 3 | 4 | impl From for super::Polarity { 5 | fn from(p: Polarity) -> Self { 6 | match p { 7 | Polarity::IdleLow => Self::IdleLow, 8 | Polarity::IdleHigh => Self::IdleHigh, 9 | } 10 | } 11 | } 12 | 13 | impl From for super::Phase { 14 | fn from(p: Phase) -> Self { 15 | match p { 16 | Phase::CaptureOnFirstTransition => Self::CaptureOnFirstTransition, 17 | Phase::CaptureOnSecondTransition => Self::CaptureOnSecondTransition, 18 | } 19 | } 20 | } 21 | 22 | impl From for super::Mode { 23 | fn from(m: Mode) -> Self { 24 | Self { 25 | polarity: m.polarity.into(), 26 | phase: m.phase.into(), 27 | } 28 | } 29 | } 30 | 31 | impl embedded_hal::spi::Error for Error { 32 | fn kind(&self) -> ErrorKind { 33 | match self { 34 | Self::Overrun => ErrorKind::Overrun, 35 | Self::ModeFault => ErrorKind::ModeFault, 36 | } 37 | } 38 | } 39 | 40 | impl ErrorType 41 | for Spi 42 | { 43 | type Error = Error; 44 | } 45 | 46 | mod nb { 47 | use super::{Error, Instance, Spi}; 48 | use embedded_hal_nb::spi::FullDuplex; 49 | 50 | impl FullDuplex 51 | for Spi 52 | where 53 | SPI: Instance, 54 | WIDTH: Copy, 55 | { 56 | fn read(&mut self) -> nb::Result { 57 | self.read_nonblocking() 58 | } 59 | 60 | fn write(&mut self, data: WIDTH) -> nb::Result<(), Error> { 61 | self.write_nonblocking(data) 62 | } 63 | } 64 | } 65 | 66 | mod blocking { 67 | use super::super::{Instance, Spi, SpiReadWrite}; 68 | use embedded_hal::spi::SpiBus; 69 | 70 | impl SpiBus 71 | for Spi 72 | where 73 | SPI: Instance, 74 | WIDTH: Copy + 'static, 75 | { 76 | fn transfer_in_place(&mut self, _words: &mut [WIDTH]) -> Result<(), Self::Error> { 77 | todo!() 78 | } 79 | 80 | fn transfer(&mut self, _buff: &mut [WIDTH], _data: &[WIDTH]) -> Result<(), Self::Error> { 81 | todo!() 82 | } 83 | 84 | fn read(&mut self, _words: &mut [WIDTH]) -> Result<(), Self::Error> { 85 | todo!() 86 | } 87 | 88 | fn write(&mut self, words: &[WIDTH]) -> Result<(), Self::Error> { 89 | self.spi_write(words) 90 | } 91 | 92 | fn flush(&mut self) -> Result<(), Self::Error> { 93 | Ok(()) 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/time.rs: -------------------------------------------------------------------------------- 1 | //! Time units 2 | //! 3 | //! See [`Hertz`], [`KiloHertz`] and [`MegaHertz`] for creating increasingly higher frequencies. 4 | //! 5 | //! The [`fugit::ExtU32`] [`U32Ext`] trait adds various methods like `.Hz()`, `.MHz()`, etc to the `u32` primitive type, 6 | //! allowing it to be converted into frequencies. 7 | //! 8 | //! # Examples 9 | //! 10 | //! ## Create a 2 MHz frequency 11 | //! 12 | //! This example demonstrates various ways of creating a 2 MHz (2_000_000 Hz) frequency. They are 13 | //! all equivalent, however the `2.MHz()` variant should be preferred for readability. 14 | //! 15 | //! ```rust 16 | //! use py32f0xx_hal::{ 17 | //! time::Hertz, 18 | //! // Imports U32Ext trait 19 | //! prelude::*, 20 | //! }; 21 | //! 22 | //! let freq_hz = 2_000_000.Hz(); 23 | //! let freq_khz = 2_000.kHz(); 24 | //! let freq_mhz = 2.MHz(); 25 | //! 26 | //! assert_eq!(freq_hz, freq_khz); 27 | //! assert_eq!(freq_khz, freq_mhz); 28 | //! ``` 29 | 30 | #![allow(non_snake_case)] 31 | 32 | use core::ops; 33 | 34 | /// Bits per second 35 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Debug)] 36 | pub struct Bps(pub u32); 37 | 38 | pub use fugit::{ 39 | HertzU32 as Hertz, KilohertzU32 as KiloHertz, MegahertzU32 as MegaHertz, 40 | MicrosDurationU32 as MicroSeconds, MillisDurationU32 as MilliSeconds, 41 | }; 42 | 43 | /// Extension trait that adds convenience methods to the `u32` type 44 | pub trait U32Ext { 45 | /// Wrap in `Bps` 46 | fn bps(self) -> Bps; 47 | } 48 | 49 | impl U32Ext for u32 { 50 | fn bps(self) -> Bps { 51 | Bps(self) 52 | } 53 | } 54 | 55 | /// Convert [u32] to [Hertz] 56 | pub const fn Hz(val: u32) -> Hertz { 57 | Hertz::from_raw(val) 58 | } 59 | 60 | /// convert [u32] to [KiloHertz] 61 | pub const fn kHz(val: u32) -> KiloHertz { 62 | KiloHertz::from_raw(val) 63 | } 64 | 65 | /// convert [u32] to [MegaHertz] 66 | pub const fn MHz(val: u32) -> MegaHertz { 67 | MegaHertz::from_raw(val) 68 | } 69 | 70 | /// convert [u32] to [MilliSeconds] 71 | pub const fn ms(val: u32) -> MilliSeconds { 72 | MilliSeconds::from_ticks(val) 73 | } 74 | 75 | /// convert [u32] to [MicroSeconds] 76 | pub const fn us(val: u32) -> MicroSeconds { 77 | MicroSeconds::from_ticks(val) 78 | } 79 | 80 | /// Macro to implement arithmetic operations (e.g. multiplication, division) 81 | /// for wrapper types. 82 | macro_rules! impl_arithmetic { 83 | ($wrapper:ty, $wrapped:ty) => { 84 | impl ops::Mul<$wrapped> for $wrapper { 85 | type Output = Self; 86 | fn mul(self, rhs: $wrapped) -> Self { 87 | Self(self.0 * rhs) 88 | } 89 | } 90 | 91 | impl ops::MulAssign<$wrapped> for $wrapper { 92 | fn mul_assign(&mut self, rhs: $wrapped) { 93 | self.0 *= rhs; 94 | } 95 | } 96 | 97 | impl ops::Div<$wrapped> for $wrapper { 98 | type Output = Self; 99 | fn div(self, rhs: $wrapped) -> Self { 100 | Self(self.0 / rhs) 101 | } 102 | } 103 | 104 | impl ops::Div<$wrapper> for $wrapper { 105 | type Output = $wrapped; 106 | fn div(self, rhs: $wrapper) -> $wrapped { 107 | self.0 / rhs.0 108 | } 109 | } 110 | 111 | impl ops::DivAssign<$wrapped> for $wrapper { 112 | fn div_assign(&mut self, rhs: $wrapped) { 113 | self.0 /= rhs; 114 | } 115 | } 116 | }; 117 | } 118 | 119 | impl_arithmetic!(Bps, u32); 120 | -------------------------------------------------------------------------------- /src/timer/counter.rs: -------------------------------------------------------------------------------- 1 | //! API for Timer Counters 2 | //! # Counters 3 | //! 4 | //! The general purpose timers and the SysTick timer can be used to 5 | //! implement periodic counting timers. 6 | //! 7 | //! ``` 8 | //! let mut timer = p.TIM3.counter_hz(&rcc.clocks); 9 | //! timer.start(1.Hz()).unwrap(); 10 | //! loop { 11 | //! // ... do something here 12 | //! nb::block!(timer.wait()).unwrap(); 13 | //! } 14 | //! ``` 15 | //! 16 | //! Using the SysTick timer 17 | //! ``` 18 | //! let cp = cortex_m::peripheral::Peripherals::take().unwrap(); 19 | //! let mut timer = cp.SYST.counter_us(&rcc.clocks); 20 | //! timer.start(10.millis()).unwrap(); 21 | //! loop { 22 | //! // ... do something here 23 | //! nb::block!(timer.wait()).unwrap(); 24 | //! } 25 | //! ``` 26 | //! 27 | 28 | use super::{compute_arr_presc, Error, Event, FTimer, Instance, SysEvent, Timer}; 29 | use crate::pac::SYST; 30 | use core::convert::TryFrom; 31 | use core::ops::{Deref, DerefMut}; 32 | use fugit::{HertzU32 as Hertz, MicrosDurationU32, TimerDurationU32, TimerInstantU32}; 33 | 34 | /// Periodic timer 35 | pub struct CounterHz(pub(super) Timer); 36 | 37 | impl Deref for CounterHz { 38 | type Target = Timer; 39 | fn deref(&self) -> &Self::Target { 40 | &self.0 41 | } 42 | } 43 | 44 | impl DerefMut for CounterHz { 45 | fn deref_mut(&mut self) -> &mut Self::Target { 46 | &mut self.0 47 | } 48 | } 49 | 50 | impl CounterHz { 51 | /// Releases the TIM peripheral 52 | pub fn release(mut self) -> Timer { 53 | // stop timer 54 | self.tim.cr1_reset(); 55 | self.0 56 | } 57 | } 58 | 59 | impl CounterHz { 60 | /// Start the counter 61 | pub fn start(&mut self, timeout: Hertz) -> Result<(), Error> { 62 | // pause 63 | self.tim.disable_counter(); 64 | 65 | self.tim.clear_interrupt_flag(Event::Update); 66 | 67 | // reset counter 68 | self.tim.reset_counter(); 69 | 70 | let (psc, arr) = compute_arr_presc(timeout.raw(), self.clk.raw()); 71 | self.tim.set_prescaler(psc); 72 | self.tim.set_auto_reload(arr)?; 73 | 74 | // Trigger update event to load the registers 75 | self.tim.trigger_update(); 76 | 77 | // start counter 78 | self.tim.enable_counter(); 79 | 80 | Ok(()) 81 | } 82 | 83 | /// Wait for counter to reach period limit, non-blocking 84 | pub fn wait(&mut self) -> nb::Result<(), Error> { 85 | if self.tim.has_interrupt_flag(Event::Update) { 86 | self.tim.clear_interrupt_flag(Event::Update); 87 | Ok(()) 88 | } else { 89 | Err(nb::Error::WouldBlock) 90 | } 91 | } 92 | 93 | /// Cancel the counter, disabling the timer 94 | pub fn cancel(&mut self) -> Result<(), Error> { 95 | if !self.tim.is_counter_enabled() { 96 | return Err(Error::Disabled); 97 | } 98 | 99 | // disable counter 100 | self.tim.disable_counter(); 101 | Ok(()) 102 | } 103 | 104 | /// Restarts the timer in count down mode with user-defined prescaler and auto-reload register 105 | pub fn start_raw(&mut self, psc: u16, arr: u16) { 106 | // pause 107 | self.tim.disable_counter(); 108 | 109 | self.tim.set_prescaler(psc); 110 | 111 | self.tim.set_auto_reload(arr as u32).unwrap(); 112 | 113 | // Trigger an update event to load the prescaler value to the clock 114 | self.tim.trigger_update(); 115 | 116 | // start counter 117 | self.tim.enable_counter(); 118 | } 119 | 120 | /// Retrieves the content of the prescaler register. The real prescaler is this value + 1. 121 | pub fn psc(&self) -> u16 { 122 | self.tim.read_prescaler() 123 | } 124 | 125 | /// Retrieves the value of the auto-reload register. 126 | pub fn arr(&self) -> u16 { 127 | TIM::read_auto_reload() as u16 128 | } 129 | 130 | /// Resets the counter 131 | pub fn reset(&mut self) { 132 | // Sets the URS bit to prevent an interrupt from being triggered by 133 | // the UG bit 134 | self.tim.trigger_update(); 135 | } 136 | 137 | /// Returns the number of microseconds since the last update event. 138 | /// *NOTE:* This method is not a very good candidate to keep track of time, because 139 | /// it is very easy to lose an update event. 140 | pub fn now(&self) -> MicrosDurationU32 { 141 | let psc = self.tim.read_prescaler() as u32; 142 | 143 | // freq_divider is always bigger than 0, since (psc + 1) is always less than 144 | // timer_clock 145 | let freq_divider = (self.clk.raw() / (psc + 1)) as u64; 146 | let cnt: u32 = self.tim.read_count().into(); 147 | let cnt = cnt as u64; 148 | 149 | // It is safe to make this cast, because the maximum timer period in this HAL is 150 | // 1s (1Hz), then 1 second < (2^32 - 1) microseconds 151 | MicrosDurationU32::from_ticks(u32::try_from(1_000_000 * cnt / freq_divider).unwrap()) 152 | } 153 | } 154 | 155 | /// Periodic non-blocking timer 156 | pub struct Counter(pub(super) FTimer); 157 | 158 | impl Deref for Counter { 159 | type Target = FTimer; 160 | fn deref(&self) -> &Self::Target { 161 | &self.0 162 | } 163 | } 164 | 165 | impl DerefMut for Counter { 166 | fn deref_mut(&mut self) -> &mut Self::Target { 167 | &mut self.0 168 | } 169 | } 170 | 171 | /// `Counter` with precision of 1 μs (1 MHz sampling) 172 | pub type CounterUs = Counter; 173 | 174 | /// `Counter` with precision of of 1 ms (1 kHz sampling) 175 | /// 176 | /// NOTE: don't use this if your system frequency more than 65 MHz 177 | pub type CounterMs = Counter; 178 | 179 | impl Counter { 180 | /// Releases the TIM peripheral 181 | pub fn release(mut self) -> FTimer { 182 | // stop counter 183 | self.tim.cr1_reset(); 184 | self.0 185 | } 186 | 187 | /// Returns a [TimerInstantU32] representing current time tick 188 | pub fn now(&self) -> TimerInstantU32 { 189 | TimerInstantU32::from_ticks(self.tim.read_count().into()) 190 | } 191 | 192 | /// Start the counter 193 | pub fn start(&mut self, timeout: TimerDurationU32) -> Result<(), Error> { 194 | // pause 195 | self.tim.disable_counter(); 196 | 197 | self.tim.clear_interrupt_flag(Event::Update); 198 | 199 | // reset counter 200 | self.tim.reset_counter(); 201 | 202 | self.tim.set_auto_reload(timeout.ticks() - 1)?; 203 | 204 | // Trigger update event to load the registers 205 | self.tim.trigger_update(); 206 | 207 | // start counter 208 | self.tim.enable_counter(); 209 | 210 | Ok(()) 211 | } 212 | 213 | /// Wait for counter to reach period limit, non-blocking 214 | pub fn wait(&mut self) -> nb::Result<(), Error> { 215 | if self.tim.has_interrupt_flag(Event::Update) { 216 | self.tim.clear_interrupt_flag(Event::Update); 217 | Ok(()) 218 | } else { 219 | Err(nb::Error::WouldBlock) 220 | } 221 | } 222 | 223 | /// Cancel the counter, disabling the timer 224 | pub fn cancel(&mut self) -> Result<(), Error> { 225 | if !self.tim.is_counter_enabled() { 226 | return Err(Error::Disabled); 227 | } 228 | 229 | // disable counter 230 | self.tim.disable_counter(); 231 | Ok(()) 232 | } 233 | } 234 | 235 | impl fugit_timer::Timer for Counter { 236 | type Error = Error; 237 | 238 | fn now(&mut self) -> TimerInstantU32 { 239 | Self::now(self) 240 | } 241 | 242 | fn start(&mut self, duration: TimerDurationU32) -> Result<(), Self::Error> { 243 | self.start(duration) 244 | } 245 | 246 | fn cancel(&mut self) -> Result<(), Self::Error> { 247 | self.cancel() 248 | } 249 | 250 | fn wait(&mut self) -> nb::Result<(), Self::Error> { 251 | self.wait() 252 | } 253 | } 254 | 255 | impl Timer { 256 | /// Creates [SysCounterHz] which takes [Hertz] as Duration 257 | pub fn counter_hz(self) -> SysCounterHz { 258 | SysCounterHz(self) 259 | } 260 | 261 | /// Creates [SysCounter] with custom precision (core frequency recommended is known) 262 | pub fn counter(self) -> SysCounter { 263 | SysCounter(self) 264 | } 265 | 266 | /// Creates [SysCounter] 1 microsecond precision 267 | pub fn counter_us(self) -> SysCounterUs { 268 | SysCounter(self) 269 | } 270 | } 271 | 272 | /// Hardware timers 273 | pub struct SysCounterHz(Timer); 274 | 275 | impl Deref for SysCounterHz { 276 | type Target = Timer; 277 | fn deref(&self) -> &Self::Target { 278 | &self.0 279 | } 280 | } 281 | 282 | impl DerefMut for SysCounterHz { 283 | fn deref_mut(&mut self) -> &mut Self::Target { 284 | &mut self.0 285 | } 286 | } 287 | 288 | impl SysCounterHz { 289 | /// Start the counter 290 | pub fn start(&mut self, timeout: Hertz) -> Result<(), Error> { 291 | let rvr = self.clk.raw() / timeout.raw() - 1; 292 | 293 | if rvr >= (1 << 24) { 294 | return Err(Error::WrongAutoReload); 295 | } 296 | 297 | self.tim.set_reload(rvr); 298 | self.tim.clear_current(); 299 | self.tim.enable_counter(); 300 | 301 | Ok(()) 302 | } 303 | 304 | /// Wait for counter period limit to be reached, non-blocking 305 | pub fn wait(&mut self) -> nb::Result<(), Error> { 306 | if self.tim.has_wrapped() { 307 | Ok(()) 308 | } else { 309 | Err(nb::Error::WouldBlock) 310 | } 311 | } 312 | 313 | /// Cancel the counter, disabling the timer 314 | pub fn cancel(&mut self) -> Result<(), Error> { 315 | if !self.tim.is_counter_enabled() { 316 | return Err(Error::Disabled); 317 | } 318 | 319 | self.tim.disable_counter(); 320 | Ok(()) 321 | } 322 | } 323 | 324 | /// Alias for [SysCounter], running at 1 μs rate 325 | pub type SysCounterUs = SysCounter<1_000_000>; 326 | 327 | /// SysTick timer with precision of 1 μs (1 MHz sampling) 328 | pub struct SysCounter(Timer); 329 | 330 | impl Deref for SysCounter { 331 | type Target = Timer; 332 | fn deref(&self) -> &Self::Target { 333 | &self.0 334 | } 335 | } 336 | 337 | impl DerefMut for SysCounter { 338 | fn deref_mut(&mut self) -> &mut Self::Target { 339 | &mut self.0 340 | } 341 | } 342 | 343 | impl SysCounter { 344 | /// Starts listening for an `event` 345 | pub fn listen(&mut self, event: SysEvent) { 346 | match event { 347 | SysEvent::Update => self.tim.enable_interrupt(), 348 | } 349 | } 350 | 351 | /// Stops listening for an `event` 352 | pub fn unlisten(&mut self, event: SysEvent) { 353 | match event { 354 | SysEvent::Update => self.tim.disable_interrupt(), 355 | } 356 | } 357 | 358 | /// Returns a [TimerInstantU32] representing current time tick 359 | pub fn now(&self) -> TimerInstantU32 { 360 | TimerInstantU32::from_ticks(SYST::get_current() / (self.clk.raw() / FREQ)) 361 | } 362 | 363 | /// Start the counter 364 | pub fn start(&mut self, timeout: TimerDurationU32) -> Result<(), Error> { 365 | let rvr = timeout.ticks() * (self.clk.raw() / FREQ) - 1; 366 | 367 | if rvr >= (1 << 24) { 368 | return Err(Error::WrongAutoReload); 369 | } 370 | 371 | self.tim.set_reload(rvr); 372 | self.tim.clear_current(); 373 | self.tim.enable_counter(); 374 | 375 | Ok(()) 376 | } 377 | 378 | /// Wait for counter to reach period limit, non-blocking 379 | pub fn wait(&mut self) -> nb::Result<(), Error> { 380 | if self.tim.has_wrapped() { 381 | Ok(()) 382 | } else { 383 | Err(nb::Error::WouldBlock) 384 | } 385 | } 386 | 387 | /// Cancel the counter, disabling the timer 388 | pub fn cancel(&mut self) -> Result<(), Error> { 389 | if !self.tim.is_counter_enabled() { 390 | return Err(Error::Disabled); 391 | } 392 | 393 | self.tim.disable_counter(); 394 | Ok(()) 395 | } 396 | } 397 | 398 | impl fugit_timer::Timer for SysCounter { 399 | type Error = Error; 400 | 401 | fn now(&mut self) -> TimerInstantU32 { 402 | Self::now(self) 403 | } 404 | 405 | fn start(&mut self, duration: TimerDurationU32) -> Result<(), Self::Error> { 406 | self.start(duration) 407 | } 408 | 409 | fn wait(&mut self) -> nb::Result<(), Self::Error> { 410 | self.wait() 411 | } 412 | 413 | fn cancel(&mut self) -> Result<(), Self::Error> { 414 | self.cancel() 415 | } 416 | } 417 | -------------------------------------------------------------------------------- /src/timer/delay.rs: -------------------------------------------------------------------------------- 1 | //! API for Timer Delays 2 | //! # Delay 3 | //! 4 | //! The general purpose timers and the SysTick timer can be used to 5 | //! implement delay timers 6 | //! 7 | //! ``` 8 | //! let mut delay = p.TIM16.delay_us(&rcc.clocks); 9 | //! loop { 10 | //! // ... do something here 11 | //! delay.delay(10.micros()); 12 | //! } 13 | //! ``` 14 | //! 15 | //! Using the SysTick timer 16 | //! ``` 17 | //! let cp = cortex_m::peripheral::Peripherals::take().unwrap(); 18 | //! let mut delay = cp.SYST.delay(&rcc.clocks); 19 | //! loop { 20 | //! // ... do something here 21 | //! delay.delay(10.millis()); 22 | //! } 23 | //! ``` 24 | //! 25 | 26 | use super::{Event, FTimer, Instance, OnePulseMode, Timer}; 27 | use core::ops::{Deref, DerefMut}; 28 | use cortex_m::peripheral::SYST; 29 | use fugit::{MicrosDurationU32, TimerDurationU32}; 30 | 31 | /// Timer as a delay provider (SysTick by default) 32 | pub struct SysDelay(Timer); 33 | 34 | impl Deref for SysDelay { 35 | type Target = Timer; 36 | fn deref(&self) -> &Self::Target { 37 | &self.0 38 | } 39 | } 40 | 41 | impl DerefMut for SysDelay { 42 | fn deref_mut(&mut self) -> &mut Self::Target { 43 | &mut self.0 44 | } 45 | } 46 | 47 | impl SysDelay { 48 | /// Releases the timer resource 49 | pub fn release(self) -> Timer { 50 | self.0 51 | } 52 | } 53 | 54 | impl Timer { 55 | /// Create a [SysDelay] instance 56 | pub fn delay(self) -> SysDelay { 57 | SysDelay(self) 58 | } 59 | } 60 | 61 | impl SysDelay { 62 | /// Delay in μs, blocking 63 | pub fn delay(&mut self, us: MicrosDurationU32) { 64 | // The SysTick Reload Value register supports values between 1 and 0x00FFFFFF. 65 | const MAX_RVR: u32 = 0x00FF_FFFF; 66 | 67 | let mut total_rvr = us.ticks() * (self.clk.raw() / 1_000_000); 68 | 69 | while total_rvr != 0 { 70 | let current_rvr = total_rvr.min(MAX_RVR); 71 | 72 | self.tim.set_reload(current_rvr); 73 | self.tim.clear_current(); 74 | self.tim.enable_counter(); 75 | 76 | // Update the tracking variable while we are waiting... 77 | total_rvr -= current_rvr; 78 | 79 | while !self.tim.has_wrapped() {} 80 | 81 | self.tim.disable_counter(); 82 | } 83 | } 84 | } 85 | 86 | /// Periodic non-blocking timer that implements [embedded_hal_02::blocking::delay] traits 87 | pub struct Delay(pub(super) FTimer); 88 | 89 | impl Deref for Delay { 90 | type Target = FTimer; 91 | fn deref(&self) -> &Self::Target { 92 | &self.0 93 | } 94 | } 95 | 96 | impl DerefMut for Delay { 97 | fn deref_mut(&mut self) -> &mut Self::Target { 98 | &mut self.0 99 | } 100 | } 101 | 102 | /// `Delay` with precision of 1 μs (1 MHz sampling) 103 | pub type DelayUs = Delay; 104 | 105 | /// `Delay` with precision of 1 ms (1 kHz sampling) 106 | /// 107 | /// NOTE: don't use this if your system frequency more than 65 MHz 108 | pub type DelayMs = Delay; 109 | 110 | impl Delay { 111 | /// Sleep for given time 112 | pub fn delay(&mut self, time: TimerDurationU32) { 113 | let mut ticks = time.ticks().max(1) - 1; 114 | while ticks != 0 { 115 | let reload = ticks.min(TIM::max_auto_reload()); 116 | 117 | // Write Auto-Reload Register (ARR) 118 | unsafe { 119 | self.tim.set_auto_reload_unchecked(reload); 120 | } 121 | 122 | // Trigger update event (UEV) in the event generation register (EGR) 123 | // in order to immediately apply the config 124 | self.tim.trigger_update(); 125 | 126 | // enable the counter. 127 | self.tim.enable_counter(); 128 | 129 | // Update the tracking variable while we are waiting... 130 | ticks -= reload; 131 | // Wait for update to happen 132 | while !self.tim.has_interrupt_flag(Event::Update) { /* wait */ } 133 | // disable the counter 134 | self.tim.disable_counter(); 135 | } 136 | } 137 | 138 | /// Get the Maximum delay possible, for use in `delay` 139 | pub fn max_delay(&self) -> TimerDurationU32 { 140 | TimerDurationU32::from_ticks(TIM::max_auto_reload()) 141 | } 142 | 143 | /// Releases the TIM peripheral 144 | pub fn release(mut self) -> FTimer { 145 | // stop counter 146 | self.tim.cr1_reset(); 147 | self.0 148 | } 149 | } 150 | 151 | /// Periodic non-blocking timer that implements [embedded_hal_02::blocking::delay] traits 152 | pub struct OpmDelay(pub(super) FTimer); 153 | 154 | impl Deref for OpmDelay { 155 | type Target = FTimer; 156 | fn deref(&self) -> &Self::Target { 157 | &self.0 158 | } 159 | } 160 | 161 | impl DerefMut for OpmDelay { 162 | fn deref_mut(&mut self) -> &mut Self::Target { 163 | &mut self.0 164 | } 165 | } 166 | 167 | /// `Delay` with precision of 1 μs (1 MHz sampling) 168 | pub type OpmDelayUs = OpmDelay; 169 | 170 | /// `Delay` with precision of 1 ms (1 kHz sampling) 171 | /// 172 | /// NOTE: don't use this if your system frequency more than 65 MHz 173 | pub type OpmDelayMs = OpmDelay; 174 | 175 | impl OpmDelay { 176 | fn delay(&mut self, time: TimerDurationU32) { 177 | let mut ticks = time.ticks().max(1) - 1; 178 | while ticks != 0 { 179 | let reload = ticks.min(TIM::max_auto_reload()); 180 | 181 | // Write Auto-Reload Register (ARR) 182 | unsafe { 183 | self.tim.set_auto_reload_unchecked(reload); 184 | } 185 | 186 | // Trigger update event (UEV) in the event generation register (EGR) 187 | // in order to immediately apply the config 188 | self.tim.trigger_update(); 189 | 190 | // Configure the counter in one-pulse mode (counter stops counting at 191 | // the next updateevent, clearing the CEN bit) and enable the counter. 192 | self.tim.start_one_pulse(); 193 | 194 | // Update the tracking variable while we are waiting... 195 | ticks -= reload; 196 | // Wait for CEN bit to clear 197 | while self.tim.is_counter_enabled() { /* wait */ } 198 | } 199 | } 200 | 201 | /// Get the maximum delay possible, for use in `delay` 202 | pub fn max_delay(&self) -> TimerDurationU32 { 203 | TimerDurationU32::from_ticks(TIM::max_auto_reload()) 204 | } 205 | 206 | /// Releases the TIM peripheral 207 | pub fn release(mut self) -> FTimer { 208 | // stop counter 209 | self.tim.cr1_reset(); 210 | self.0 211 | } 212 | } 213 | 214 | impl fugit_timer::Delay for Delay { 215 | type Error = core::convert::Infallible; 216 | 217 | fn delay(&mut self, duration: TimerDurationU32) -> Result<(), Self::Error> { 218 | self.delay(duration); 219 | Ok(()) 220 | } 221 | } 222 | 223 | impl fugit_timer::Delay 224 | for OpmDelay 225 | { 226 | type Error = core::convert::Infallible; 227 | 228 | fn delay(&mut self, duration: TimerDurationU32) -> Result<(), Self::Error> { 229 | self.delay(duration); 230 | Ok(()) 231 | } 232 | } 233 | 234 | impl fugit_timer::Delay<1_000_000> for SysDelay { 235 | type Error = core::convert::Infallible; 236 | 237 | fn delay(&mut self, duration: MicrosDurationU32) -> Result<(), Self::Error> { 238 | self.delay(duration); 239 | Ok(()) 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/timer/hal_02.rs: -------------------------------------------------------------------------------- 1 | //! Delay implementation based on general-purpose 16 bit timers and System timer (SysTick). 2 | //! 3 | //! TIM1 and TIM3 are a general purpose 16-bit auto-reload up/downcounter with 4 | //! a 16-bit prescaler. 5 | 6 | use crate::time::Hertz; 7 | use embedded_hal_02::{ 8 | blocking::delay::{DelayMs, DelayUs}, 9 | timer::{Cancel, CountDown, Periodic}, 10 | }; 11 | use fugit::{ExtU32, TimerDurationU32}; 12 | use void::Void; 13 | 14 | use super::{ 15 | Channel, Counter, CounterHz, Delay, Error, Instance, OcPin, Pins, PwmChannel, PwmHz, 16 | SysCounter, SysCounterHz, SysDelay, WithPwm, 17 | }; 18 | 19 | impl DelayUs for SysDelay { 20 | fn delay_us(&mut self, us: u32) { 21 | self.delay(us.micros()) 22 | } 23 | } 24 | 25 | impl DelayMs for SysDelay { 26 | fn delay_ms(&mut self, ms: u32) { 27 | self.delay_us(ms * 1_000); 28 | } 29 | } 30 | 31 | impl DelayUs for SysDelay { 32 | fn delay_us(&mut self, us: u16) { 33 | self.delay_us(us as u32) 34 | } 35 | } 36 | 37 | impl DelayMs for SysDelay { 38 | fn delay_ms(&mut self, ms: u16) { 39 | self.delay_ms(ms as u32); 40 | } 41 | } 42 | 43 | impl DelayUs for SysDelay { 44 | fn delay_us(&mut self, us: u8) { 45 | self.delay_us(us as u32) 46 | } 47 | } 48 | 49 | impl DelayMs for SysDelay { 50 | fn delay_ms(&mut self, ms: u8) { 51 | self.delay_ms(ms as u32); 52 | } 53 | } 54 | 55 | impl Periodic for CounterHz {} 56 | impl Periodic for SysCounterHz {} 57 | impl Periodic for SysCounter {} 58 | 59 | impl CountDown for SysCounterHz { 60 | type Time = Hertz; 61 | 62 | fn start(&mut self, timeout: T) 63 | where 64 | T: Into, 65 | { 66 | self.start(timeout.into()).unwrap() 67 | } 68 | 69 | fn wait(&mut self) -> nb::Result<(), Void> { 70 | match self.wait() { 71 | Err(nb::Error::WouldBlock) => Err(nb::Error::WouldBlock), 72 | _ => Ok(()), 73 | } 74 | } 75 | } 76 | 77 | impl Cancel for SysCounterHz { 78 | type Error = Error; 79 | 80 | fn cancel(&mut self) -> Result<(), Self::Error> { 81 | self.cancel() 82 | } 83 | } 84 | 85 | impl CountDown for CounterHz { 86 | type Time = Hertz; 87 | 88 | fn start(&mut self, timeout: T) 89 | where 90 | T: Into, 91 | { 92 | self.start(timeout.into()).unwrap() 93 | } 94 | 95 | fn wait(&mut self) -> nb::Result<(), Void> { 96 | match self.wait() { 97 | Err(nb::Error::WouldBlock) => Err(nb::Error::WouldBlock), 98 | _ => Ok(()), 99 | } 100 | } 101 | } 102 | 103 | impl Cancel for CounterHz { 104 | type Error = Error; 105 | 106 | fn cancel(&mut self) -> Result<(), Self::Error> { 107 | self.cancel() 108 | } 109 | } 110 | 111 | impl CountDown for SysCounter { 112 | type Time = TimerDurationU32; 113 | 114 | fn start(&mut self, timeout: T) 115 | where 116 | T: Into, 117 | { 118 | self.start(timeout.into()).unwrap() 119 | } 120 | 121 | fn wait(&mut self) -> nb::Result<(), Void> { 122 | match self.wait() { 123 | Err(nb::Error::WouldBlock) => Err(nb::Error::WouldBlock), 124 | _ => Ok(()), 125 | } 126 | } 127 | } 128 | 129 | impl Cancel for SysCounter { 130 | type Error = Error; 131 | 132 | fn cancel(&mut self) -> Result<(), Self::Error> { 133 | self.cancel() 134 | } 135 | } 136 | 137 | impl embedded_hal_02::PwmPin for PwmChannel { 138 | type Duty = u16; 139 | 140 | fn disable(&mut self) { 141 | self.disable() 142 | } 143 | fn enable(&mut self) { 144 | self.enable() 145 | } 146 | fn get_duty(&self) -> Self::Duty { 147 | self.get_duty() 148 | } 149 | fn get_max_duty(&self) -> Self::Duty { 150 | self.get_max_duty() 151 | } 152 | fn set_duty(&mut self, duty: Self::Duty) { 153 | self.set_duty(duty) 154 | } 155 | } 156 | 157 | impl embedded_hal_02::Pwm for PwmHz 158 | where 159 | TIM: Instance + WithPwm, 160 | PINS: Pins, 161 | { 162 | type Channel = Channel; 163 | type Duty = u16; 164 | type Time = Hertz; 165 | 166 | fn enable(&mut self, channel: Self::Channel) { 167 | self.enable(channel as _) 168 | } 169 | 170 | fn disable(&mut self, channel: Self::Channel) { 171 | self.disable(channel as _) 172 | } 173 | 174 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { 175 | self.get_duty(channel as _) 176 | } 177 | 178 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { 179 | self.set_duty(channel as _, duty) 180 | } 181 | 182 | /// If `0` returned means max_duty is 2^16 183 | fn get_max_duty(&self) -> Self::Duty { 184 | self.get_max_duty() 185 | } 186 | 187 | fn get_period(&self) -> Self::Time { 188 | self.get_period() 189 | } 190 | 191 | fn set_period(&mut self, period: T) 192 | where 193 | T: Into, 194 | { 195 | self.set_period(period.into()) 196 | } 197 | } 198 | 199 | impl DelayUs for Delay { 200 | /// Sleep for `us` microseconds 201 | fn delay_us(&mut self, us: u32) { 202 | self.delay(us.micros()) 203 | } 204 | } 205 | 206 | impl DelayMs for Delay { 207 | /// Sleep for `ms` milliseconds 208 | fn delay_ms(&mut self, ms: u32) { 209 | self.delay(ms.millis()) 210 | } 211 | } 212 | 213 | impl DelayUs for Delay { 214 | /// Sleep for `us` microseconds 215 | fn delay_us(&mut self, us: u16) { 216 | self.delay((us as u32).micros()) 217 | } 218 | } 219 | impl DelayMs for Delay { 220 | /// Sleep for `ms` milliseconds 221 | fn delay_ms(&mut self, ms: u16) { 222 | self.delay((ms as u32).millis()) 223 | } 224 | } 225 | 226 | impl DelayUs for Delay { 227 | /// Sleep for `us` microseconds 228 | fn delay_us(&mut self, us: u8) { 229 | self.delay((us as u32).micros()) 230 | } 231 | } 232 | impl DelayMs for Delay { 233 | /// Sleep for `ms` milliseconds 234 | fn delay_ms(&mut self, ms: u8) { 235 | self.delay((ms as u32).millis()) 236 | } 237 | } 238 | 239 | impl Periodic for Counter {} 240 | 241 | impl CountDown for Counter { 242 | type Time = TimerDurationU32; 243 | 244 | fn start(&mut self, timeout: T) 245 | where 246 | T: Into, 247 | { 248 | self.start(timeout.into()).unwrap() 249 | } 250 | 251 | fn wait(&mut self) -> nb::Result<(), Void> { 252 | match self.wait() { 253 | Err(nb::Error::WouldBlock) => Err(nb::Error::WouldBlock), 254 | _ => Ok(()), 255 | } 256 | } 257 | } 258 | 259 | impl Cancel for Counter { 260 | type Error = Error; 261 | 262 | fn cancel(&mut self) -> Result<(), Self::Error> { 263 | self.cancel() 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /src/timer/hal_1.rs: -------------------------------------------------------------------------------- 1 | //! Delay implementation based on general-purpose 16 bit timers and System timer (SysTick). 2 | //! 3 | //! TIM1 and TIM3 are a general purpose 16-bit auto-reload up/downcounter with 4 | //! a 16-bit prescaler. 5 | 6 | use embedded_hal::delay::DelayNs; 7 | 8 | use super::{Delay, Instance, OcPin, PwmChannel, SysDelay, WithPwm}; 9 | use core::convert::Infallible; 10 | use fugit::ExtU32Ceil; 11 | 12 | impl DelayNs for SysDelay { 13 | fn delay_ns(&mut self, ns: u32) { 14 | self.delay(ns.nanos_at_least()); 15 | } 16 | 17 | fn delay_ms(&mut self, ms: u32) { 18 | self.delay(ms.millis_at_least()); 19 | } 20 | } 21 | 22 | impl DelayNs for Delay { 23 | fn delay_ns(&mut self, ns: u32) { 24 | self.delay(ns.micros_at_least()); 25 | } 26 | 27 | fn delay_us(&mut self, us: u32) { 28 | self.delay(us.micros_at_least()); 29 | } 30 | 31 | fn delay_ms(&mut self, ms: u32) { 32 | self.delay(ms.millis_at_least()); 33 | } 34 | } 35 | 36 | impl embedded_hal::pwm::ErrorType for PwmChannel { 37 | type Error = Infallible; 38 | } 39 | 40 | impl embedded_hal::pwm::SetDutyCycle for PwmChannel { 41 | fn max_duty_cycle(&self) -> u16 { 42 | self.get_max_duty() 43 | } 44 | fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { 45 | self.set_duty(duty); 46 | Ok(()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/timer/monotonic.rs: -------------------------------------------------------------------------------- 1 | //! RTIC Monotonic implementation 2 | //! 3 | //! To enable this monotonic timer implementation, use the feature `rtic` 4 | 5 | use super::{FTimer, Instance}; 6 | use crate::rcc::Clocks; 7 | use core::ops::{Deref, DerefMut}; 8 | use rtic_monotonic::Monotonic; 9 | 10 | /// Monotonic Timer for RTIC 11 | pub struct MonoTimer { 12 | timer: FTimer, 13 | ovf: u32, 14 | } 15 | 16 | impl Deref for MonoTimer { 17 | type Target = FTimer; 18 | fn deref(&self) -> &Self::Target { 19 | &self.timer 20 | } 21 | } 22 | 23 | impl DerefMut for MonoTimer { 24 | fn deref_mut(&mut self) -> &mut Self::Target { 25 | &mut self.timer 26 | } 27 | } 28 | 29 | /// `MonoTimer` with precision of 1 μs (1 MHz sampling) 30 | pub type MonoTimerUs = MonoTimer; 31 | 32 | impl MonoTimer { 33 | /// Releases the TIM peripheral 34 | pub fn release(mut self) -> FTimer { 35 | // stop counter 36 | self.tim.cr1_reset(); 37 | self.timer 38 | } 39 | } 40 | 41 | /// Extension trait for Timer peripheral 42 | pub trait MonoTimerExt: Sized { 43 | /// Make a [MonoTimer] instance 44 | fn monotonic(self, clocks: &Clocks) -> MonoTimer; 45 | /// Make a [MonoTimer] instance running at 1 μs 46 | fn monotonic_us(self, clocks: &Clocks) -> MonoTimer { 47 | self.monotonic::<1_000_000>(clocks) 48 | } 49 | } 50 | 51 | macro_rules! mono { 52 | ($TIM:ty) => { 53 | impl MonoTimerExt for $TIM { 54 | fn monotonic(self, clocks: &Clocks) -> MonoTimer { 55 | FTimer::new(self, clocks).monotonic() 56 | } 57 | } 58 | 59 | impl FTimer<$TIM, FREQ> { 60 | /// Make a [MonoTimer] instance 61 | pub fn monotonic(self) -> MonoTimer<$TIM, FREQ> { 62 | MonoTimer::<$TIM, FREQ>::_new(self) 63 | } 64 | } 65 | 66 | impl MonoTimer<$TIM, FREQ> { 67 | fn _new(timer: FTimer<$TIM, FREQ>) -> Self { 68 | // Set auto-reload value. 69 | timer.tim.arr.write(|w| unsafe { w.arr().bits(u16::MAX) }); 70 | // Generate interrupt on overflow. 71 | timer.tim.egr.write(|w| w.ug().set_bit()); 72 | 73 | // Start timer. 74 | // Clear interrupt flag. 75 | timer.tim.sr.modify(|_, w| w.uif().clear_bit()); 76 | timer.tim.cr1.modify(|_, w| { 77 | // Enable counter. 78 | w.cen().set_bit(); 79 | // Overflow should trigger update event. 80 | w.udis().clear_bit(); 81 | // Only overflow triggers interrupt. 82 | w.urs().set_bit() 83 | }); 84 | 85 | Self { timer, ovf: 0 } 86 | } 87 | } 88 | 89 | impl Monotonic for MonoTimer<$TIM, FREQ> { 90 | type Instant = fugit::TimerInstantU32; 91 | type Duration = fugit::TimerDurationU32; 92 | 93 | unsafe fn reset(&mut self) { 94 | self.tim.dier.modify(|_, w| w.cc1ie().set_bit()); 95 | } 96 | 97 | #[inline(always)] 98 | fn now(&mut self) -> Self::Instant { 99 | let cnt = self.tim.cnt.read().cnt().bits() as u32; 100 | 101 | // If the overflow bit is set, we add this to the timer value. It means the `on_interrupt` 102 | // has not yet happened, and we need to compensate here. 103 | let ovf = if self.tim.sr.read().uif().bit_is_set() { 104 | 0x10000 105 | } else { 106 | 0 107 | }; 108 | 109 | Self::Instant::from_ticks(cnt.wrapping_add(ovf).wrapping_add(self.ovf)) 110 | } 111 | 112 | fn set_compare(&mut self, instant: Self::Instant) { 113 | let now = self.now(); 114 | let cnt = self.tim.cnt.read().cnt().bits(); 115 | 116 | // Since the timer may or may not overflow based on the requested compare val, we check 117 | // how many ticks are left. 118 | let val = match instant.checked_duration_since(now) { 119 | None => cnt.wrapping_add(0xffff), // In the past, RTIC will handle this 120 | Some(x) if x.ticks() <= 0xffff => instant.duration_since_epoch().ticks() as u16, // Will not overflow 121 | Some(_) => cnt.wrapping_add(0xffff), // Will overflow, run for as long as possible 122 | }; 123 | 124 | #[cfg(any(feature = "py32f030", feature = "py32f003"))] 125 | self.tim.ccr[0].write(|w| unsafe { w.ccr1().bits(val) }); 126 | #[cfg(any(feature = "py32f002a", feature = "py32f002b"))] 127 | self.tim.ccr[0].write(|w| unsafe { w.ccr().bits(val) }); 128 | } 129 | 130 | fn clear_compare_flag(&mut self) { 131 | self.tim.sr.modify(|_, w| w.cc1if().clear_bit()); 132 | } 133 | 134 | fn on_interrupt(&mut self) { 135 | // If there was an overflow, increment the overflow counter. 136 | if self.tim.sr.read().uif().bit_is_set() { 137 | self.tim.sr.modify(|_, w| w.uif().clear_bit()); 138 | 139 | self.ovf += 0x10000; 140 | } 141 | } 142 | 143 | #[inline(always)] 144 | fn zero() -> Self::Instant { 145 | Self::Instant::from_ticks(0) 146 | } 147 | } 148 | }; 149 | } 150 | 151 | mono!(crate::pac::TIM1); 152 | 153 | #[cfg(any(feature = "py32f030", feature = "py32f003"))] 154 | mono!(crate::pac::TIM3); 155 | #[cfg(any(feature = "py32f030", feature = "py32f003"))] 156 | mono!(crate::pac::TIM17); 157 | 158 | #[cfg(any(feature = "py32f030", feature = "py32f003"))] 159 | mono!(crate::pac::TIM16); 160 | 161 | #[cfg(any(feature = "py32f030", feature = "py32f003", feature = "py32f002b"))] 162 | mono!(crate::pac::TIM14); 163 | -------------------------------------------------------------------------------- /src/timer/pins.rs: -------------------------------------------------------------------------------- 1 | use crate::gpio::AF2; 2 | use crate::gpio::{gpioa::*, gpiob::*, Alternate}; 3 | use crate::pac; 4 | 5 | #[cfg(any(feature = "py32f030", feature = "py32f003", feature = "py32f002a"))] 6 | use crate::gpio::{AF1, AF13, AF14}; 7 | 8 | #[cfg(any(feature = "py32f030", feature = "py32f003", feature = "py32f002b"))] 9 | use crate::gpio::AF5; 10 | 11 | #[cfg(any(feature = "py32f030", feature = "py32f003"))] 12 | use crate::gpio::{gpiof::*, AF0, AF4}; 13 | 14 | #[cfg(feature = "py32f002b")] 15 | use crate::gpio::{gpioc::*, AF3}; 16 | 17 | // Output channels marker traits 18 | /// Timer channel 1 Output Pin 19 | pub trait PinC1 {} 20 | /// Timer channel 1 Complementary Pin 21 | pub trait PinC1N {} 22 | /// Timer channel 2 Output Pin 23 | pub trait PinC2 {} 24 | /// Timer channel 2 Complementary Pin 25 | pub trait PinC2N {} 26 | /// Timer channel 3 Output Pin 27 | pub trait PinC3 {} 28 | /// Timer channel 3 Complementary Pin 29 | pub trait PinC3N {} 30 | /// Timer channel 4 Output Pin 31 | pub trait PinC4 {} 32 | 33 | macro_rules! channel_impl { 34 | ( $( $TIM:ty, $PINC:ident, $PINX:ident, $MODE:ident<$AF:ident>; )+ ) => { 35 | $( 36 | impl $PINC<$TIM> for $PINX<$MODE<$AF>> {} 37 | )+ 38 | }; 39 | } 40 | 41 | #[cfg(any(feature = "py32f030", feature = "py32f003", feature = "py32f002a"))] 42 | channel_impl!( 43 | pac::TIM1, PinC3, PA0, Alternate; 44 | pac::TIM1, PinC1N, PA0, Alternate; 45 | pac::TIM1, PinC4, PA1, Alternate; 46 | pac::TIM1, PinC2N, PA1, Alternate; 47 | pac::TIM1, PinC1, PA3, Alternate; 48 | pac::TIM1, PinC1N, PA7, Alternate; 49 | pac::TIM1, PinC2, PA13, Alternate; 50 | pac::TIM1, PinC2N, PB0, Alternate; 51 | pac::TIM1, PinC3N, PB1, Alternate; 52 | pac::TIM1, PinC3, PB6, Alternate; 53 | ); 54 | 55 | #[cfg(any(feature = "py32f030", feature = "py32f002a"))] 56 | channel_impl!( 57 | pac::TIM1, PinC1, PA8, Alternate; 58 | pac::TIM1, PinC2, PA9, Alternate; 59 | pac::TIM1, PinC3, PA10, Alternate; 60 | pac::TIM1, PinC4, PA11, Alternate; 61 | pac::TIM1, PinC2, PB3, Alternate; 62 | ); 63 | 64 | #[cfg(any(feature = "py32f030", feature = "py32f003"))] 65 | channel_impl!( 66 | pac::TIM3, PinC1, PA2, Alternate; 67 | pac::TIM3, PinC3, PA4, Alternate; 68 | pac::TIM3, PinC2, PA5, Alternate; 69 | pac::TIM3, PinC1, PA6, Alternate; 70 | pac::TIM3, PinC2, PA7, Alternate; 71 | pac::TIM3, PinC3, PB0, Alternate; 72 | pac::TIM3, PinC4, PB1, Alternate; 73 | pac::TIM3, PinC2, PB5, Alternate; 74 | 75 | pac::TIM14, PinC1, PA4, Alternate; 76 | pac::TIM14, PinC1, PA7, Alternate; 77 | pac::TIM14, PinC1, PB1, Alternate; 78 | pac::TIM14, PinC1, PF0, Alternate; 79 | pac::TIM14, PinC1, PF1, Alternate; 80 | 81 | pac::TIM16, PinC1, PA6, Alternate; 82 | pac::TIM16, PinC1N, PB6, Alternate; 83 | 84 | pac::TIM17, PinC1, PA7, Alternate; 85 | pac::TIM17, PinC1N, PB7, Alternate; 86 | ); 87 | 88 | #[cfg(feature = "py32f030")] 89 | channel_impl!( 90 | pac::TIM3, PinC1, PB4, Alternate; 91 | 92 | pac::TIM16, PinC1, PB8, Alternate; 93 | 94 | pac::TIM17, PinC1, PB8, Alternate; 95 | ); 96 | 97 | #[cfg(feature = "py32f002b")] 98 | channel_impl!( 99 | pac::TIM1, PinC1, PA0, Alternate; 100 | pac::TIM1, PinC2, PA1, Alternate; 101 | pac::TIM1, PinC4, PA2, Alternate; 102 | pac::TIM1, PinC2, PA3, Alternate; 103 | pac::TIM1, PinC3, PA4, Alternate; 104 | pac::TIM1, PinC1, PA5, Alternate; 105 | pac::TIM1, PinC4, PA7, Alternate; 106 | pac::TIM1, PinC2, PB0, Alternate; 107 | pac::TIM1, PinC3N, PB0, Alternate; 108 | pac::TIM1, PinC2N, PB1, Alternate; 109 | pac::TIM1, PinC4, PB1, Alternate; 110 | pac::TIM1, PinC1N, PB2, Alternate; 111 | pac::TIM1, PinC3, PB2, Alternate; 112 | pac::TIM1, PinC3, PB5, Alternate; 113 | pac::TIM1, PinC1N, PC0, Alternate; 114 | 115 | pac::TIM14, PinC1, PA4, Alternate; 116 | pac::TIM14, PinC1, PA5, Alternate; 117 | pac::TIM14, PinC1, PB5, Alternate; 118 | pac::TIM14, PinC1, PB7, Alternate; 119 | ); 120 | -------------------------------------------------------------------------------- /src/watchdog.rs: -------------------------------------------------------------------------------- 1 | //! API for the IWDG (Watchdog) 2 | //! 3 | //! You can activate the watchdog by calling `start` or by setting the appropriate 4 | //! device option bit when programming. 5 | //! 6 | //! After activating the watchdog, you'll have to regularly `feed` the watchdog. 7 | //! If more time than `timeout` has gone by since the last `feed`, your 8 | //! microcontroller will be reset. 9 | //! 10 | //! This is useful if you fear that your program may get stuck. In that case it 11 | //! won't feed the watchdog anymore, the watchdog will reset the microcontroller 12 | //! and thus your program will function again. 13 | //! 14 | //! **Attention**: 15 | //! 16 | //! The IWDG runs on a separate 40kHz low-accuracy clock (30kHz-60kHz). You may 17 | //! want to some buffer in your interval. 18 | //! 19 | //! Per default the iwdg continues to run even when you stopped execution of code via a debugger. 20 | //! You may want to disable the watchdog when the cpu is stopped 21 | //! 22 | //! ``` ignore 23 | //! let dbgmcu = p.DBGMCU; 24 | //! dbgmcu.apb1_fz.modify(|_, w| w.dbg_iwdg_stop().set_bit()); 25 | //! ``` 26 | //! 27 | //! # Example 28 | //! ``` no_run 29 | //! use py32f0xx_hal as hal; 30 | //! 31 | //! use crate::hal::pac; 32 | //! use crate::hal::prelude::*; 33 | //! use crate::hal:watchdog::Watchdog; 34 | //! 35 | //! let mut p = pac::Peripherals::take().unwrap(); 36 | //! let mut rcc = p.RCC.configure().sysclk(8.MHz()).freeze(&mut p.FLASH); 37 | //! 38 | //! let mut iwdg = Watchdog::new(&mut rcc, p.iwdg); 39 | //! iwdg.start(100.Hz()); 40 | //! loop {} 41 | //! // Whoops, got stuck, the watchdog issues a reset after 10 ms 42 | //! iwdg.feed(); 43 | //! ``` 44 | use embedded_hal_02::watchdog; 45 | 46 | use crate::pac::IWDG; 47 | use crate::rcc::Rcc; 48 | use crate::time::Hertz; 49 | 50 | /// Watchdog instance 51 | pub struct Watchdog { 52 | iwdg: IWDG, 53 | } 54 | 55 | impl watchdog::Watchdog for Watchdog { 56 | /// Feed the watchdog, so that at least one `period` goes by before the next 57 | /// reset 58 | fn feed(&mut self) { 59 | self.iwdg.kr.write(|w| w.key().reset()); 60 | } 61 | } 62 | 63 | /// Timeout configuration for the IWDG 64 | #[derive(PartialEq, PartialOrd, Clone, Copy)] 65 | pub struct IwdgTimeout { 66 | psc: u8, 67 | reload: u16, 68 | } 69 | 70 | impl From for IwdgTimeout { 71 | /// This converts the value so it's usable by the IWDG 72 | /// Due to conversion losses, the specified frequency is a maximum 73 | /// 74 | /// It can also only represent values < 10000 Hertz 75 | fn from(hz: Hertz) -> Self { 76 | let mut time = 32_768 / 4 / hz.raw(); 77 | let mut psc = 0; 78 | let mut reload = 0; 79 | while psc < 7 { 80 | reload = time; 81 | if reload < 0x1000 { 82 | break; 83 | } 84 | psc += 1; 85 | time /= 2; 86 | } 87 | // As we get an integer value, reload is always below 0xFFF 88 | let reload = reload as u16; 89 | IwdgTimeout { psc, reload } 90 | } 91 | } 92 | 93 | impl Watchdog { 94 | /// Create a new [Watchdog] 95 | /// 96 | /// Modifies the RCC registers to turn on LSI clock 97 | pub fn new(rcc: &mut Rcc, iwdg: IWDG) -> Self { 98 | rcc.regs.csr.modify(|_, w| w.lsion().on()); 99 | while rcc.regs.csr.read().lsirdy().is_not_ready() {} 100 | Self { iwdg } 101 | } 102 | } 103 | 104 | impl watchdog::WatchdogEnable for Watchdog { 105 | type Time = IwdgTimeout; 106 | fn start(&mut self, period: T) 107 | where 108 | T: Into, 109 | { 110 | let time: IwdgTimeout = period.into(); 111 | // Feed the watchdog in case it's already running 112 | // (Waiting for the registers to update takes sometime) 113 | self.iwdg.kr.write(|w| w.key().reset()); 114 | // Enable the watchdog 115 | self.iwdg.kr.write(|w| w.key().start()); 116 | self.iwdg.kr.write(|w| w.key().enable()); 117 | // Wait until it's safe to write to the registers 118 | while self.iwdg.sr.read().pvu().bit() {} 119 | self.iwdg.pr.write(|w| w.pr().bits(time.psc)); 120 | while self.iwdg.sr.read().rvu().bit() {} 121 | self.iwdg.rlr.write(|w| w.rl().bits(time.reload)); 122 | // Wait until the registers are updated before issuing a reset with 123 | // (potentially false) values 124 | while self.iwdg.sr.read().bits() != 0 {} 125 | self.iwdg.kr.write(|w| w.key().reset()); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /tools/capture_example_bloat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | version="stable" 4 | features="py32f030,rt" 5 | 6 | filename="bloat_log_"$version"_"`date -Iminutes`".txt" 7 | filenamenoopt="bloat_noopt_log_"$version"_"`date -Iminutes`".txt" 8 | 9 | cargo +$version rustc -- -V >>$filename 10 | cargo +$version rustc -- -V >>$filenamenoopt 11 | 12 | for i in `find examples -name "*.rs"`; do 13 | name=$(echo $i | sed -e "s,examples/,,g" -e "s,\.rs,,g") 14 | echo "Processing example $name" 15 | 16 | echo >>$filename 17 | echo "Bloat for example $name" >>$filename 18 | cargo +$version bloat --release --example $name --features="$features" -n 60 >>$filename 19 | echo >>$filename 20 | echo "Section sizes: " >>$filename 21 | cargo +$version size --release --example $name --features="$features" >>$filename 22 | 23 | echo >>$filenamenoopt 24 | echo "Bloat for example $name" >>$filenamenoopt 25 | cargo +$version bloat --example $name --features="$features" -n 60 >>$filenamenoopt 26 | echo >>$filenamenoopt 27 | echo "Section size: " >>$filenamenoopt 28 | cargo +$version size --example $name --features="$features" >>$filenamenoopt 29 | done 30 | 31 | echo "Captured bloat for rustc version \"$version\" for all examples with features \"$features\" into $filename and $filenamenoopt" 32 | -------------------------------------------------------------------------------- /tools/capture_nightly_example_bloat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | version="nightly" 4 | features="py32f030,rt" 5 | 6 | filename="bloat_log_"$version"_"`date -Iminutes`".txt" 7 | filenamenoopt="bloat_noopt_log_"$version"_"`date -Iminutes`".txt" 8 | 9 | cargo +$version rustc -- -V >>$filename 10 | cargo +$version rustc -- -V >>$filenamenoopt 11 | 12 | for i in `find examples -name "*.rs"`; do 13 | name=$(echo $i | sed -e "s,examples/,,g" -e "s,\.rs,,g") 14 | echo "Processing example $name" 15 | 16 | echo >>$filename 17 | echo "Bloat for example $name" >>$filename 18 | cargo +$version bloat --release --example $name --features="$features" -n 60 >>$filename 19 | echo >>$filename 20 | echo "Section sizes: " >>$filename 21 | cargo +$version size --release --example $name --features="$features" >>$filename 22 | 23 | echo >>$filenamenoopt 24 | echo "Bloat for example $name" >>$filenamenoopt 25 | cargo +$version bloat --example $name --features="$features" -n 60 >>$filenamenoopt 26 | echo >>$filenamenoopt 27 | echo "Section size: " >>$filenamenoopt 28 | cargo +$version size --example $name --features="$features" >>$filenamenoopt 29 | done 30 | 31 | echo "Captured bloat for rustc version \"$version\" for all examples with features \"$features\" into $filename and $filenamenoopt" 32 | -------------------------------------------------------------------------------- /tools/check.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import json 4 | import subprocess 5 | import sys 6 | 7 | 8 | def run_inner(args): 9 | print("Running `{}`...".format(" ".join(args))) 10 | ret = subprocess.call(args) == 0 11 | print("") 12 | return ret 13 | 14 | 15 | def run(mcu, cargo_cmd): 16 | if mcu == "": 17 | return run_inner(cargo_cmd) 18 | else: 19 | return run_inner(cargo_cmd + ["--features={}".format(mcu)]) 20 | 21 | 22 | def main(): 23 | cargo_meta = json.loads( 24 | subprocess.check_output("cargo metadata --no-deps --format-version=1", 25 | shell=True, 26 | universal_newlines=True) 27 | ) 28 | 29 | crate_info = cargo_meta["packages"][0] 30 | features = [ 31 | "{},defmt,rt,rtic".format(x) 32 | for x in crate_info["features"].keys() 33 | if x != "device-selected" 34 | and x != "rt" 35 | and x != "rtic" 36 | and x != "defmt" 37 | and x != "with-dma" 38 | and x != "py32f030" 39 | and x != "py32f003" 40 | and x != "py32f002a" 41 | and x != "py32f002b" 42 | and not x.startswith("flash") 43 | and not x.startswith("ram") 44 | ] 45 | 46 | if 'size_check' in sys.argv: 47 | cargo_cmd = ['cargo', 'build', '--release'] 48 | else: 49 | cargo_cmd = ['cargo', 'check'] 50 | 51 | if not all(map(lambda f: run(f, cargo_cmd), 52 | features)): 53 | sys.exit(-1) 54 | 55 | 56 | if __name__ == "__main__": 57 | main() 58 | --------------------------------------------------------------------------------