├── .cargo └── config.toml ├── .github └── workflows │ ├── ci.yml │ └── clippy.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.rs ├── examples ├── blinky-timer-irq.rs ├── blinky.rs ├── can-echo.rs ├── can-loopback.rs ├── delay-syst-blinky.rs ├── delay-timer-blinky.rs ├── exti_button.rs ├── flash.rs ├── fmc.rs ├── hello.rs ├── i2c_scanner.rs ├── pwm-sinus.rs ├── pwm.rs ├── rng.rs ├── rtc.rs ├── rtic-tick.rs ├── serial_delay.rs ├── serial_dma.rs ├── serial_echo.rs ├── serial_parity.rs ├── spi.rs ├── spi_16.rs ├── spi_dma.rs ├── spi_dma_16.rs ├── stm32f7disco-qspi-flash │ ├── main.rs │ └── mt25q.rs ├── stm32f7disco-screen │ ├── main.rs │ └── screen.rs ├── timer-periph.rs ├── timer-syst.rs └── usb_serial.rs ├── memory_1024_320.x ├── memory_2048_368.x ├── memory_512_176.x ├── memory_64_176.x ├── memory_64_240.x ├── openocd.cfg ├── openocd.gdb ├── release.toml └── src ├── adc.rs ├── can.rs ├── dac.rs ├── dma.rs ├── flash.rs ├── fmc.rs ├── fmc_lcd ├── display_interface_impl.rs ├── mod.rs ├── pins.rs ├── sealed.rs └── timing.rs ├── gpio.rs ├── gpio ├── convert.rs ├── dynamic.rs ├── erased.rs ├── hal_02.rs └── partially_erased.rs ├── i2c.rs ├── lib.rs ├── ltdc.rs ├── otg_fs.rs ├── otg_hs.rs ├── prelude.rs ├── qei.rs ├── qspi.rs ├── rcc.rs ├── rcc └── enable.rs ├── rng.rs ├── rtc.rs ├── serial.rs ├── signature.rs ├── spi.rs ├── timer.rs ├── timer ├── counter.rs ├── delay.rs ├── hal_02.rs ├── monotonic.rs ├── pins.rs ├── pwm.rs └── pwm_input.rs └── watchdog.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.thumbv7em-none-eabihf] 2 | runner = "arm-none-eabi-gdb -q -tui -x openocd.gdb" 3 | rustflags = [ 4 | "-C", "link-arg=-Tlink.x", 5 | ] 6 | 7 | [build] 8 | target = "thumbv7em-none-eabihf" 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Continuous integration 4 | 5 | jobs: 6 | ci: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | mcu: 11 | - stm32f722 12 | - stm32f723 13 | - stm32f732 14 | - stm32f733 15 | - stm32f745 16 | - stm32f746 17 | - stm32f756 18 | - stm32f765 19 | - stm32f767 20 | - stm32f769 21 | - stm32f777 22 | - stm32f778 23 | - stm32f779 24 | rust: 25 | - stable 26 | include: 27 | - rust: nightly 28 | mcu: stm32f746 29 | experimental: true 30 | 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: dtolnay/rust-toolchain@master 34 | with: 35 | toolchain: ${{ matrix.rust }} 36 | targets: thumbv7em-none-eabihf 37 | - run: cargo build --features=${{ matrix.mcu }},usb_fs --examples 38 | - run: cargo build --features=${{ matrix.mcu }},usb_fs --examples --release 39 | - run: cargo build --features=${{ matrix.mcu }},rt,usb_fs --examples 40 | - run: cargo build --features=${{ matrix.mcu }},rt,usb_hs --examples 41 | - run: cargo test --features=${{ matrix.mcu }} --target x86_64-unknown-linux-gnu --lib 42 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Clippy 4 | 5 | jobs: 6 | clippy_check: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: dtolnay/rust-toolchain@master 11 | with: 12 | components: clippy 13 | toolchain: 1.80.0 14 | targets: thumbv7em-none-eabihf 15 | # TODO: For now, allow clippy::erasing_op 16 | # TODO: Enable clippy on examples via `--examples` 17 | - run: cargo clippy --target thumbv7em-none-eabihf --features=rt,stm32f746 -- --allow clippy::erasing_op 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.orig 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust.all_targets": false, 3 | "rust.target": "thumbv7em-none-eabihf", 4 | "rust.all_features": false, 5 | "rust.features": [ 6 | "rtic", 7 | "stm32f767" 8 | ], 9 | "rust-analyzer.checkOnSave.allTargets": false, 10 | "rust-analyzer.checkOnSave.extraArgs": [ 11 | "--target", 12 | "thumbv7em-none-eabihf" 13 | ], 14 | "rust-analyzer.cargo.features": [ 15 | "rtic", 16 | "stm32f767" 17 | ] 18 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | 9 | ## [Unreleased] - ReleaseDate 10 | 11 | ### Fixed 12 | 13 | - Use the right addresses for `signature` module structs on f7x2, f7x3, and f730 devices 14 | 15 | ## [0.8.0] - 2024-08-02 16 | 17 | ### Changed 18 | 19 | - Updated `stm32f7` dependency 0.14.0 -> 0.15 20 | - Use PascalCase for generated values of enums ([stm32-rs#727](https://github.com/stm32-rs/stm32-rs/pull/727)) 21 | - Updated `synopsys-usb-otg` dependency 0.2.3 -> 0.4.0 22 | - Updated `stm32-fmc` dependency 0.2.0 -> 0.3 23 | - Added Interruptable trait to Alternate mode pins 24 | - Added a "low pin count" variant of the f730 chip to the crate features: packages <144 pins don't include a high speed USB PHY 25 | - Added SPI2_SCK pin for stm32f769i-discovery 26 | - Fix mass-erase triggering in `flash` on smaller chips 27 | - Remove the need for software u64 division in the clock setup code, shrinking code (#211) 28 | - Updated `cortex-m` dependency 0.7.4 -> 0.7.7 29 | - Updated `nb` dependency 1.0 -> 1.1.0 30 | - Updated `micromath` dependency 2.0 -> 2.1.0 31 | - Updated `fugit` dependency 0.3.5 -> 0.3.7 32 | - Updated `bitflags` dependency 1.3.2 -> 2.6.0 33 | - Updated `embedded-hal` dependency 0.2.3 -> 0.2.7 34 | - Updated `display-interface` dependency 0.4.1 -> 0.5.0 35 | - Updated `cortex-m-semihosting` development dependency 0.3.3 -> 0.5.0 36 | - Removed unwrap from the end of hprintln / hprint for new version of semihosting 37 | - Updated `panic-semihosting` development dependency 0.5.2 -> 0.6.0 38 | - Updated `embedded-graphics` development dependency 0.6.1 -> 0.6.2 39 | - Updated `usb-device` development dependency 0.2.5 -> 0.3.2 40 | - Updated `usbd-serial` development dependency 0.1.0 -> 0.2.2 41 | - Updated usb serial example to use new api 42 | - Renamed .cargo/config -> .cargo/config.toml 43 | 44 | ## [v0.7.0] - 2022-06-05 45 | 46 | ### Added 47 | 48 | - Support for `flash` on bigger chips (#168) 49 | - Implement `IndependentWatchdog` for the IWDG peripheral (#180) 50 | - Implement `embedded-hal` 0.2 features for `gpio` and `timer` (#176) 51 | - Support for different number of data and parity bits for UART (#181/#182) 52 | - Support for PWM, counter, monotonic on timers (#179) 53 | - Examples: 54 | - [Basic use of the PWM](examples/pwm.rs) (#179) 55 | - [Basic use of the RTC](examples/pwm-sinus.rs) (#159) 56 | - [Blinking a LED using a delay from a timer source](examples/delay-timer-blinky.rs) (#179) 57 | - [Blinking a LED from within a timer interrupt](examples/blinky-timer-irq.rs) (#179) 58 | - [Blinking a LED from RTIC using a timer as monotonic source](examples/rtic-tick.rs) (#179) 59 | - [Scanning I2C devices](examples/i2c_scanner.rs) (#155) 60 | - [Generating a sine wave using PWM](examples/pwm-sinus.rs) (#179) 61 | - [Using bit parity on UART](examples/serial_parity.rs) (#182) 62 | - [Using a timer using `nb::block!()`](examples/timer-periph.rs) (#179) 63 | - [Using the system timer using `nb::block!()`](examples/timer-syst.rs) (#179) 64 | 65 | ### Changed 66 | 67 | - Renamed `master` branch to `main` [Updating a local clone after a branch name changes](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-branches-in-your-repository/renaming-a-branch#updating-a-local-clone-after-a-branch-name-changes) 68 | - Split `GetBusFreq` on `BusClock` & `BusTimerClock`, use `&Clock` everywhere (#178) 69 | - Use `fugit`-based time types instead of `embedded-time` (#177) 70 | - Update gpios: add `DynamicPin`, add default modes, reexport pins, resort generics, etc (#176) 71 | - Improved RCC infrastructure (#152) 72 | - RTC support has been rewritten (#159/#160/162) 73 | - Bump `bxcan` dependency version (#158) 74 | - Removed `rustfmt` checks on CI (#184) 75 | 76 | ### Fixed 77 | 78 | - Fix RAM address and add ITCM and DTCM sections (#156) 79 | - Fix default mode for debug pins (#166) 80 | - Use `BitsPerSeconds` instead of `BytesPerSecond` in the serial baud rate configuration (#175) 81 | 82 | ## [v0.6.0] - 2021-11-02 83 | 84 | ## [v0.5.0] - 2021-09-22 85 | 86 | ## [v0.4.0] - 2021-07-16 87 | 88 | ## [v0.3.0] - 2021-04-26 89 | 90 | ## [v0.2.0] - 2020-07-01 91 | 92 | ## [v0.1.0] - 2019-11-05 93 | 94 | 95 | 96 | [Unreleased]: https://github.com/stm32-rs/stm32f7xx-hal/compare/v0.8.0...HEAD 97 | [0.8.0]: https://github.com/stm32-rs/stm32f7xx-hal/compare/v0.7.0...v0.8.0 98 | [v0.7.0]: https://github.com/stm32-rs/stm32f7xx-hal/compare/v0.6.0...v0.7.0 99 | [v0.6.0]: https://github.com/stm32-rs/stm32f7xx-hal/compare/v0.5.0...v0.6.0 100 | [v0.5.0]: https://github.com/stm32-rs/stm32f7xx-hal/compare/v0.4.0...v0.5.0 101 | [v0.4.0]: https://github.com/stm32-rs/stm32f7xx-hal/compare/v0.3.0...v0.4.0 102 | [v0.3.0]: https://github.com/stm32-rs/stm32f7xx-hal/compare/v0.2.0...v0.3.0 103 | [v0.2.0]: https://github.com/stm32-rs/stm32f7xx-hal/compare/v0.1.0...v0.2.0 104 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | rust-version = "1.59" 4 | 5 | authors = ["Matt Vertescher "] 6 | categories = ["embedded", "hardware-support", "no-std"] 7 | description = "HAL for the STM32F7xx family of microcontrollers" 8 | documentation = "https://docs.rs/stm32f7xx-hal" 9 | keywords = ["arm", "cortex-m", "stm32f7xx", "hal"] 10 | license = "MIT OR Apache-2.0" 11 | name = "stm32f7xx-hal" 12 | readme = "README.md" 13 | repository = "https://github.com/stm32-rs/stm32f7xx-hal" 14 | version = "0.8.0" 15 | 16 | [package.metadata.docs.rs] 17 | features = ["stm32f746", "rt"] 18 | 19 | [dependencies] 20 | as-slice = "0.2" 21 | cortex-m = "0.7.7" 22 | cortex-m-rt = ">=0.6.15, <0.8" 23 | nb = "1.1.0" 24 | stm32f7 = "0.15" 25 | micromath = "2.1.0" 26 | synopsys-usb-otg = { version = "0.4.0", features = ["cortex-m"], optional = true } 27 | stm32-fmc = { version = "0.3", features = ["sdram"], optional = true } 28 | rand_core = "0.6" 29 | bxcan = "0.7" 30 | bare-metal = "1.0" 31 | fugit = "0.3.7" 32 | fugit-timer = "0.1.3" 33 | bitflags = "2.6.0" 34 | rtic-monotonic = { version = "1.0", optional = true } 35 | 36 | [dependencies.time] 37 | version = "0.3" 38 | default-features = false 39 | 40 | [dependencies.cast] 41 | default-features = false 42 | version = "0.3.0" 43 | 44 | [dependencies.embedded-hal] 45 | features = ["unproven"] 46 | version = "0.2.7" 47 | 48 | [dependencies.void] 49 | default-features = false 50 | version = "1.0.2" 51 | 52 | [dependencies.display-interface] 53 | optional = true 54 | version = "0.5.0" 55 | 56 | [dev-dependencies] 57 | cortex-m-semihosting = "0.5.0" 58 | panic-halt = "0.2.0" 59 | panic-semihosting = "0.6.0" 60 | embedded-graphics = "0.6.2" 61 | usb-device = "0.3.2" 62 | usbd-serial = "0.2.2" 63 | 64 | [dev-dependencies.time] 65 | version = "0.3" 66 | default-features = false 67 | features = ["macros"] 68 | 69 | [features] 70 | device-selected = [] 71 | ltdc = [] 72 | fmc = ["stm32-fmc"] 73 | usb_hs_phy = [] 74 | rt = ["stm32f7/rt"] 75 | 76 | svd-f730 = ["stm32f7/stm32f730"] 77 | svd-f745 = ["stm32f7/stm32f745"] 78 | svd-f765 = ["stm32f7/stm32f765"] 79 | svd-f7x2 = ["stm32f7/stm32f7x2"] 80 | svd-f7x3 = ["stm32f7/stm32f7x3"] 81 | svd-f7x6 = ["stm32f7/stm32f7x6"] 82 | svd-f7x7 = ["stm32f7/stm32f7x7"] 83 | svd-f7x9 = ["stm32f7/stm32f7x9"] 84 | 85 | stm32f722 = ["svd-f7x2", "device-selected"] 86 | stm32f723 = ["svd-f7x3", "device-selected", "usb_hs_phy"] 87 | stm32f730 = ["svd-f730", "device-selected", "usb_hs_phy", "fmc"] 88 | stm32f730-lpc = ["svd-f730", "device-selected", "fmc"] 89 | stm32f732 = ["svd-f7x2", "device-selected"] 90 | stm32f733 = ["svd-f7x3", "device-selected", "usb_hs_phy"] 91 | stm32f745 = ["svd-f745", "device-selected", "gpioj", "gpiok", "fmc"] 92 | stm32f746 = ["svd-f7x6", "device-selected", "gpioj", "gpiok", "ltdc", "fmc", "has-can"] 93 | stm32f756 = ["svd-f7x6", "device-selected", "gpioj", "gpiok", "ltdc", "fmc"] 94 | stm32f765 = ["svd-f765", "device-selected", "gpioj", "gpiok", "fmc"] 95 | stm32f767 = ["svd-f7x7", "device-selected", "gpioj", "gpiok", "ltdc", "fmc"] 96 | stm32f769 = ["svd-f7x9", "device-selected", "gpioj", "gpiok", "ltdc", "fmc"] 97 | stm32f777 = ["svd-f7x7", "device-selected", "gpioj", "gpiok", "ltdc", "fmc"] 98 | stm32f778 = ["svd-f7x9", "device-selected", "gpioj", "gpiok", "ltdc", "fmc"] 99 | stm32f779 = ["svd-f7x9", "device-selected", "gpioj", "gpiok", "ltdc", "fmc"] 100 | 101 | fmc_lcd = ["display-interface"] 102 | 103 | usb_fs = ["synopsys-usb-otg", "synopsys-usb-otg/fs"] 104 | usb_hs = ["synopsys-usb-otg", "synopsys-usb-otg/hs"] 105 | 106 | has-can = [] 107 | 108 | gpioj = [] 109 | gpiok = [] 110 | 111 | rtic = ["rt", "rtic-monotonic"] 112 | 113 | [profile.dev] 114 | incremental = false 115 | codegen-units = 1 116 | 117 | [profile.release] 118 | codegen-units = 1 119 | debug = true 120 | lto = true 121 | 122 | [[example]] 123 | name = "blinky" 124 | required-features = ["device-selected", "rt"] 125 | 126 | [[example]] 127 | name = "delay-syst-blinky" 128 | required-features = ["device-selected"] 129 | 130 | [[example]] 131 | name = "delay-timer-blinky" 132 | required-features = ["device-selected"] 133 | 134 | [[example]] 135 | name = "flash" 136 | required-features = ["stm32f746", "rt"] 137 | 138 | [[example]] 139 | name = "fmc" 140 | required-features = ["stm32f746", "rt", "fmc"] 141 | 142 | [[example]] 143 | name = "hello" 144 | required-features = ["stm32f746", "rt"] 145 | 146 | [[example]] 147 | name = "rtic-tick" 148 | required-features = ["device-selected", "rtic"] 149 | 150 | [[example]] 151 | name = "serial_delay" 152 | required-features = ["stm32f746", "rt"] 153 | 154 | [[example]] 155 | name = "serial_echo" 156 | required-features = ["stm32f746", "rt"] 157 | 158 | [[example]] 159 | name = "serial_parity" 160 | required-features = ["stm32f767", "rt"] 161 | 162 | [[example]] 163 | name = "stm32f7disco-screen" 164 | required-features = ["stm32f746", "rt"] 165 | 166 | [[example]] 167 | name = "exti_button" 168 | required-features = ["stm32f767", "rt"] 169 | 170 | [[example]] 171 | name = "usb_serial" 172 | required-features = ["stm32f723", "rt", "synopsys-usb-otg"] 173 | 174 | [[example]] 175 | name = "rng" 176 | 177 | [[example]] 178 | name = "stm32f7disco-qspi-flash" 179 | required-features = ["stm32f746", "rt"] 180 | 181 | [[example]] 182 | name = "can-echo" 183 | required-features = ["has-can"] 184 | 185 | [[example]] 186 | name = "can-loopback" 187 | required-features = ["has-can"] 188 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Matt Vertescher 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `stm32f7xx-hal` 2 | 3 | > [HAL] for the STM32F7 family of microcontrollers 4 | 5 | [HAL]: https://crates.io/crates/embedded-hal 6 | 7 | [![Crates.io - stm32f7xx-hal](https://img.shields.io/crates/v/stm32f7xx-hal.svg?maxAge=2592000)](https://crates.io/crates/stm32f7xx-hal) 8 | [![Released API docs](https://docs.rs/stm32f7xx-hal/badge.svg)](https://docs.rs/stm32f7xx-hal) 9 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 10 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 11 | [![CI](https://github.com/stm32-rs/stm32f7xx-hal/workflows/Continuous%20integration/badge.svg?branch=master)](https://github.com/stm32-rs/stm32f7xx-hal/actions) 12 | 13 | This crate is largely inspired by the awesome work done here: 14 | 15 | - [stm32f1xx-hal](https://github.com/stm32-rs/stm32f1xx-hal) 16 | - [stm32f4xx-hal](https://github.com/stm32-rs/stm32f4xx-hal) 17 | 18 | ## Selecting a microcontroller 19 | 20 | This crate supports multiple microcontrollers in the 21 | stm32f7 family. Which specific microcontroller you want to build for has to be 22 | specified with a feature, for example `stm32f767`. 23 | 24 | If no microcontroller is specified, the crate will not compile. 25 | 26 | ### Supported Microcontrollers 27 | 28 | * `stm32f722` 29 | * `stm32f723` 30 | * `stm32f730` 31 | * `stm32f730-lpc` ("low pin count" variant) 32 | * `stm32f732` 33 | * `stm32f733` 34 | * `stm32f745` 35 | * `stm32f746` 36 | * `stm32f756` 37 | * `stm32f765` 38 | * `stm32f767` 39 | * `stm32f769` 40 | * `stm32f777` 41 | * `stm32f778` 42 | * `stm32f779` 43 | 44 | ## Using as a Dependency 45 | 46 | When using this crate as a dependency in your project, the microcontroller can 47 | be specified as part of the `Cargo.toml` definition. 48 | 49 | ```toml 50 | [dependencies.stm32f7xx-hal] 51 | version = "0.7.0" 52 | features = ["stm32f767", "rt"] 53 | ``` 54 | 55 | ## Documentation 56 | 57 | The documentation can be found at [docs.rs/stm32f7xx-hal](https://docs.rs/stm32f7xx-hal/). 58 | 59 | ## VSCode 60 | 61 | Default settings for `rust-analyzer` are set in [.vscode/settings.json](.vscode/settings.json) for `stm32f767`. If you're working on another chip, you can change the target there for convenience, but don't commit your change to this file. 62 | 63 | ## License 64 | 65 | Licensed under either of 66 | 67 | * Apache License, Version 2.0 68 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 69 | * MIT license 70 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 71 | 72 | at your option. 73 | 74 | ## Contribution 75 | 76 | Unless you explicitly state otherwise, any contribution intentionally submitted 77 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 78 | dual licensed as above, without any additional terms or conditions. 79 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, 3 | fs::File, 4 | io::{self, prelude::*}, 5 | path::PathBuf, 6 | }; 7 | 8 | fn main() -> Result<(), Error> { 9 | let target = Target::read(); 10 | 11 | copy_memory_config(target)?; 12 | 13 | println!("cargo:rerun-if-changed=build.rs"); 14 | 15 | Ok(()) 16 | } 17 | 18 | /// Make `memory.x` available to dependent crates 19 | fn copy_memory_config(target: Target) -> Result<(), Error> { 20 | let memory_x = match target.sub_family { 21 | SubFamily::Stm32f722 22 | | SubFamily::Stm32f723 23 | | SubFamily::Stm32f732 24 | | SubFamily::Stm32f733 => include_bytes!("memory_512_176.x").as_ref(), 25 | SubFamily::Stm32f745 | SubFamily::Stm32f746 | SubFamily::Stm32f756 => { 26 | include_bytes!("memory_1024_320.x").as_ref() 27 | } 28 | SubFamily::Stm32f765 29 | | SubFamily::Stm32f767 30 | | SubFamily::Stm32f769 31 | | SubFamily::Stm32f777 32 | | SubFamily::Stm32f778 33 | | SubFamily::Stm32f779 => include_bytes!("memory_2048_368.x").as_ref(), 34 | SubFamily::Stm32f730 => include_bytes!("memory_64_176.x").as_ref(), 35 | }; 36 | 37 | let out_dir = env::var("OUT_DIR")?; 38 | let out_dir = PathBuf::from(out_dir); 39 | 40 | File::create(out_dir.join("memory.x"))?.write_all(memory_x)?; 41 | 42 | // Tell Cargo where to find the file. 43 | println!("cargo:rustc-link-search={}", out_dir.display()); 44 | 45 | println!("cargo:rerun-if-changed=memory_1024_320.x"); 46 | println!("cargo:rerun-if-changed=memory_2048_368.x"); 47 | println!("cargo:rerun-if-changed=memory_512_176.x"); 48 | println!("cargo:rerun-if-changed=memory_64_176.x"); 49 | println!("cargo:rerun-if-changed=memory_64_240.x"); 50 | 51 | Ok(()) 52 | } 53 | 54 | #[derive(Clone, Copy)] 55 | struct Target { 56 | sub_family: SubFamily, 57 | } 58 | 59 | impl Target { 60 | fn read() -> Self { 61 | let sub_family = SubFamily::read(); 62 | 63 | Self { sub_family } 64 | } 65 | } 66 | 67 | #[derive(Clone, Copy)] 68 | enum SubFamily { 69 | Stm32f722, 70 | Stm32f723, 71 | Stm32f730, 72 | Stm32f732, 73 | Stm32f733, 74 | Stm32f745, 75 | Stm32f746, 76 | Stm32f756, 77 | Stm32f765, 78 | Stm32f767, 79 | Stm32f769, 80 | Stm32f777, 81 | Stm32f778, 82 | Stm32f779, 83 | } 84 | 85 | impl SubFamily { 86 | fn read() -> Self { 87 | if cfg!(feature = "stm32f722") { 88 | SubFamily::Stm32f722 89 | } else if cfg!(feature = "stm32f723") { 90 | SubFamily::Stm32f723 91 | } else if cfg!(feature = "stm32f730") || cfg!(feature = "stm32f730-lpc") { 92 | SubFamily::Stm32f730 93 | } else if cfg!(feature = "stm32f732") { 94 | SubFamily::Stm32f732 95 | } else if cfg!(feature = "stm32f733") { 96 | SubFamily::Stm32f733 97 | } else if cfg!(feature = "stm32f745") { 98 | SubFamily::Stm32f745 99 | } else if cfg!(feature = "stm32f746") { 100 | SubFamily::Stm32f746 101 | } else if cfg!(feature = "stm32f756") { 102 | SubFamily::Stm32f756 103 | } else if cfg!(feature = "stm32f765") { 104 | SubFamily::Stm32f765 105 | } else if cfg!(feature = "stm32f767") { 106 | SubFamily::Stm32f767 107 | } else if cfg!(feature = "stm32f769") { 108 | SubFamily::Stm32f769 109 | } else if cfg!(feature = "stm32f777") { 110 | SubFamily::Stm32f777 111 | } else if cfg!(feature = "stm32f778") { 112 | SubFamily::Stm32f778 113 | } else if cfg!(feature = "stm32f779") { 114 | SubFamily::Stm32f779 115 | } else { 116 | error("You must select a target. 117 | If you added Stm32f7xx HAL as a dependency to your crate, you can select a target by enabling the respective feature in `Cargo.toml`. 118 | If you're running an example from the repository, select a target by passing the desired target as a command-line argument, for example `--features=stm32f746`. 119 | Please refer to the documentation for more details." 120 | ) 121 | } 122 | } 123 | } 124 | 125 | #[derive(Debug)] 126 | enum Error { 127 | Env(env::VarError), 128 | Io(io::Error), 129 | } 130 | 131 | impl From for Error { 132 | fn from(error: env::VarError) -> Self { 133 | Self::Env(error) 134 | } 135 | } 136 | 137 | impl From for Error { 138 | fn from(error: io::Error) -> Self { 139 | Self::Io(error) 140 | } 141 | } 142 | 143 | fn error(message: &str) -> ! { 144 | panic!("\n\n\n{}\n\n\n", message); 145 | } 146 | -------------------------------------------------------------------------------- /examples/blinky-timer-irq.rs: -------------------------------------------------------------------------------- 1 | //! blinky timer using interrupts on TIM2, adapted from blinky_timer_irq.rs example from 2 | //! stm32f1xx-hal 3 | //! 4 | //! This assumes that a LED is connected to pa5 (sck/d13) as is the case on most nucleo board. 5 | 6 | #![no_main] 7 | #![no_std] 8 | 9 | use panic_halt as _; 10 | 11 | use stm32f7xx_hal as hal; 12 | 13 | use crate::hal::{ 14 | gpio::{self, Output, PushPull}, 15 | pac::{interrupt, Interrupt, Peripherals, TIM2}, 16 | prelude::*, 17 | timer::{CounterUs, Event}, 18 | }; 19 | 20 | use core::cell::RefCell; 21 | use cortex_m::interrupt::Mutex; 22 | use cortex_m_rt::entry; 23 | 24 | // NOTE You can uncomment 'hprintln' here and in the code below for a bit more 25 | // verbosity at runtime, at the cost of throwing off the timing of the blink 26 | // (using 'semihosting' for printing debug info anywhere slows program 27 | // execution down) 28 | //use cortex_m_semihosting::hprintln; 29 | 30 | // A type definition for the GPIO pin to be used for our LED 31 | // For the onboard nucleo LED, use PA5 or PB13 depending your model 32 | type LedPin = gpio::PA5>; 33 | 34 | // Make LED pin globally available 35 | static G_LED: Mutex>> = Mutex::new(RefCell::new(None)); 36 | 37 | // Make timer interrupt registers globally available 38 | static G_TIM: Mutex>>> = Mutex::new(RefCell::new(None)); 39 | 40 | // Define an interupt handler, i.e. function to call when interrupt occurs. 41 | // This specific interrupt will "trip" when the timer TIM2 times out 42 | #[interrupt] 43 | fn TIM2() { 44 | static mut LED: Option = None; 45 | static mut TIM: Option> = None; 46 | 47 | let led = LED.get_or_insert_with(|| { 48 | cortex_m::interrupt::free(|cs| { 49 | // Move LED pin here, leaving a None in its place 50 | G_LED.borrow(cs).replace(None).unwrap() 51 | }) 52 | }); 53 | 54 | let tim = TIM.get_or_insert_with(|| { 55 | cortex_m::interrupt::free(|cs| { 56 | // Move LED pin here, leaving a None in its place 57 | G_TIM.borrow(cs).replace(None).unwrap() 58 | }) 59 | }); 60 | 61 | let _ = led.toggle(); 62 | let _ = tim.wait(); 63 | } 64 | 65 | #[entry] 66 | fn main() -> ! { 67 | let dp = Peripherals::take().unwrap(); 68 | 69 | let rcc = dp.RCC.constrain(); 70 | let clocks = rcc.cfgr.sysclk(16.MHz()).pclk1(13.MHz()).freeze(); 71 | 72 | // Configure PA5 pin to blink LED 73 | let gpioa = dp.GPIOA.split(); 74 | let mut led = gpioa.pa5.into_push_pull_output(); 75 | let _ = led.set_high(); // Turn off 76 | 77 | // Move the pin into our global storage 78 | cortex_m::interrupt::free(|cs| *G_LED.borrow(cs).borrow_mut() = Some(led)); 79 | 80 | // Set up a timer expiring after 1s 81 | let mut timer = dp.TIM2.counter(&clocks); 82 | timer.start(1.secs()).unwrap(); 83 | 84 | // Generate an interrupt when the timer expires 85 | timer.listen(Event::Update); 86 | 87 | // Move the timer into our global storage 88 | cortex_m::interrupt::free(|cs| *G_TIM.borrow(cs).borrow_mut() = Some(timer)); 89 | 90 | //enable TIM2 interrupt 91 | unsafe { 92 | cortex_m::peripheral::NVIC::unmask(Interrupt::TIM2); 93 | } 94 | 95 | #[allow(clippy::empty_loop)] 96 | loop { 97 | // Uncomment if you want to make controller sleep 98 | // cortex_m::asm::wfi(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /examples/blinky.rs: -------------------------------------------------------------------------------- 1 | //! Blinks an LED 2 | 3 | #![deny(unsafe_code)] 4 | #![deny(warnings)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | extern crate panic_halt; 9 | 10 | use stm32f7xx_hal as hal; 11 | 12 | use crate::hal::{pac, prelude::*}; 13 | use cortex_m_rt::entry; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | let p = pac::Peripherals::take().unwrap(); 18 | 19 | let gpioi = p.GPIOI.split(); 20 | let mut led = gpioi.pi1.into_push_pull_output(); 21 | 22 | loop { 23 | for _ in 0..10_000 { 24 | led.set_high(); 25 | } 26 | for _ in 0..10_000 { 27 | led.set_low(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/can-echo.rs: -------------------------------------------------------------------------------- 1 | //! Simple CAN example. 2 | //! Requires a transceiver connected to PA11, PA12 (CAN1) or PB5 PB6 (CAN2). 3 | 4 | #![no_main] 5 | #![no_std] 6 | 7 | use bxcan::Fifo::Fifo0; 8 | use panic_halt as _; 9 | 10 | use bxcan::filter::Mask32; 11 | use cortex_m_rt::entry; 12 | use nb::block; 13 | use stm32f7xx_hal::{ 14 | can::Can, 15 | pac, 16 | prelude::*, 17 | rcc::{HSEClock, HSEClockMode}, 18 | }; 19 | 20 | #[entry] 21 | fn main() -> ! { 22 | let dp = pac::Peripherals::take().unwrap(); 23 | 24 | let mut rcc = dp.RCC.constrain(); 25 | 26 | // To meet CAN clock accuracy requirements an external crystal or ceramic 27 | // resonator must be used. The blue pill has a 8MHz external crystal. 28 | // Other boards might have a crystal with another frequency or none at all. 29 | let _clocks = rcc 30 | .cfgr 31 | .hse(HSEClock::new(25_000_000.Hz(), HSEClockMode::Bypass)) 32 | .sysclk(216_000_000.Hz()) 33 | .hclk(216_000_000.Hz()) 34 | .freeze(); 35 | 36 | let gpioa = dp.GPIOA.split(); 37 | let gpiob = dp.GPIOB.split(); 38 | 39 | let mut can1 = { 40 | let rx = gpioa.pa11.into_alternate(); 41 | let tx = gpioa.pa12.into_alternate(); 42 | 43 | let can = Can::new(dp.CAN1, &mut rcc.apb1, (tx, rx)); 44 | 45 | bxcan::Can::builder(can) 46 | // APB1 (PCLK1): 130MHz, Bit rate: 512kBit/s, Sample Point 87.5% 47 | // Value was calculated with http://www.bittiming.can-wiki.info/ 48 | .set_bit_timing(0x001e_000b) 49 | .enable() 50 | }; 51 | 52 | // Configure filters so that can frames can be received. 53 | let mut filters = can1.modify_filters(); 54 | filters.enable_bank(0, Fifo0, Mask32::accept_all()); 55 | 56 | let _can2 = { 57 | let rx = gpiob.pb5.into_alternate(); 58 | let tx = gpiob.pb6.into_alternate(); 59 | 60 | let can = Can::new(dp.CAN2, &mut rcc.apb1, (tx, rx)); 61 | 62 | let can2 = bxcan::Can::builder(can) 63 | // APB1 (PCLK1): 130MHz, Bit rate: 512kBit/s, Sample Point 87.5% 64 | // Value was calculated with http://www.bittiming.can-wiki.info/ 65 | .set_bit_timing(0x001e_000b) 66 | .enable(); 67 | 68 | // A total of 28 filters are shared between the two CAN instances. 69 | // Split them equally between CAN1 and CAN2. 70 | filters.set_split(14); 71 | let mut slave_filters = filters.slave_filters(); 72 | slave_filters.enable_bank(14, Fifo0, Mask32::accept_all()); 73 | can2 74 | }; 75 | 76 | // Drop filters to leave filter configuration mode. 77 | drop(filters); 78 | 79 | // Select the interface. 80 | let mut can = can1; 81 | //let mut can = can2; 82 | 83 | // Echo back received packages in sequence. 84 | loop { 85 | if let Ok(frame) = block!(can.receive()) { 86 | block!(can.transmit(&frame)).unwrap(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /examples/can-loopback.rs: -------------------------------------------------------------------------------- 1 | //! Showcases advanced CAN filter capabilities. 2 | //! Does not require additional transceiver hardware. 3 | 4 | #![no_main] 5 | #![no_std] 6 | 7 | use bxcan::Fifo::Fifo0; 8 | use bxcan::{ 9 | filter::{ListEntry16, ListEntry32, Mask16}, 10 | ExtendedId, Frame, StandardId, 11 | }; 12 | use panic_halt as _; 13 | 14 | use cortex_m_rt::entry; 15 | use nb::block; 16 | use stm32f7xx_hal::{ 17 | can::Can, 18 | pac, 19 | prelude::*, 20 | rcc::{HSEClock, HSEClockMode}, 21 | }; 22 | 23 | #[entry] 24 | fn main() -> ! { 25 | let dp = pac::Peripherals::take().unwrap(); 26 | 27 | let mut rcc = dp.RCC.constrain(); 28 | 29 | // To meet CAN clock accuracy requirements, an external crystal or ceramic 30 | // resonator must be used. 31 | let _clocks = rcc 32 | .cfgr 33 | .hse(HSEClock::new(25_000_000.Hz(), HSEClockMode::Bypass)) 34 | .sysclk(216_000_000.Hz()) 35 | .hclk(216_000_000.Hz()) 36 | .freeze(); 37 | 38 | let gpioa = dp.GPIOA.split(); 39 | 40 | let rx = gpioa.pa11.into_alternate(); 41 | let tx = gpioa.pa12.into_alternate(); 42 | 43 | let can = Can::new(dp.CAN1, &mut rcc.apb1, (tx, rx)); 44 | 45 | // Use loopback mode: No pins need to be assigned to peripheral. 46 | let mut can = bxcan::Can::builder(can) 47 | // APB1 (PCLK1): 130MHz, Bit rate: 512kBit/s, Sample Point 87.5% 48 | // Value was calculated with http://www.bittiming.can-wiki.info/ 49 | .set_bit_timing(0x001e_000b) 50 | .set_loopback(true) 51 | .set_silent(true) 52 | .enable(); 53 | 54 | let mut filters = can.modify_filters(); 55 | assert!(filters.num_banks() > 3); 56 | 57 | // The order of the added filters is important: it must match configuration 58 | // of the `split_filters_advanced()` method. 59 | 60 | // 2x 11bit id + mask filter bank: Matches 0, 1, 2 61 | filters.enable_bank( 62 | 0, 63 | Fifo0, 64 | [ 65 | // accepts 0 and 1 66 | Mask16::frames_with_std_id(StandardId::new(0).unwrap(), StandardId::new(1).unwrap()), 67 | // accepts 0 and 2 68 | Mask16::frames_with_std_id(StandardId::new(0).unwrap(), StandardId::new(2).unwrap()), 69 | ], 70 | ); 71 | 72 | // 2x 29bit id filter bank: Matches 4, 5 73 | filters.enable_bank( 74 | 1, 75 | Fifo0, 76 | [ 77 | ListEntry32::data_frames_with_id(ExtendedId::new(4).unwrap()), 78 | ListEntry32::data_frames_with_id(ExtendedId::new(5).unwrap()), 79 | ], 80 | ); 81 | 82 | // 4x 11bit id filter bank: Matches 8, 9, 10, 11 83 | filters.enable_bank( 84 | 2, 85 | Fifo0, 86 | [ 87 | ListEntry16::data_frames_with_id(StandardId::new(8).unwrap()), 88 | ListEntry16::data_frames_with_id(StandardId::new(9).unwrap()), 89 | ListEntry16::data_frames_with_id(StandardId::new(10).unwrap()), 90 | ListEntry16::data_frames_with_id(StandardId::new(11).unwrap()), 91 | ], 92 | ); 93 | 94 | // Drop filters to leave filter configuration mode. 95 | drop(filters); 96 | 97 | // Some messages shall pass the filters. 98 | for &id in &[0, 1, 2, 8, 9, 10, 11] { 99 | let frame_tx = Frame::new_data(StandardId::new(id).unwrap(), [id as u8]); 100 | block!(can.transmit(&frame_tx)).unwrap(); 101 | let frame_rx = block!(can.receive()).unwrap(); 102 | assert_eq!(frame_tx, frame_rx); 103 | } 104 | for &id in &[4, 5] { 105 | let frame_tx = Frame::new_data(ExtendedId::new(id).unwrap(), [id as u8]); 106 | block!(can.transmit(&frame_tx)).unwrap(); 107 | let frame_rx = block!(can.receive()).unwrap(); 108 | assert_eq!(frame_tx, frame_rx); 109 | } 110 | 111 | // Some messages shall not be received. 112 | for &id in &[3, 6, 7, 12] { 113 | let frame_tx = Frame::new_data(ExtendedId::new(id).unwrap(), [id as u8]); 114 | block!(can.transmit(&frame_tx)).unwrap(); 115 | while !can.is_transmitter_idle() {} 116 | 117 | assert!(can.receive().is_err()); 118 | } 119 | 120 | loop {} 121 | } 122 | -------------------------------------------------------------------------------- /examples/delay-syst-blinky.rs: -------------------------------------------------------------------------------- 1 | //! Demonstrate the use of a blocking `Delay` using the SYST (sysclock) timer. 2 | 3 | #![deny(unsafe_code)] 4 | #![allow(clippy::empty_loop)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | // Halt on panic 9 | use panic_halt as _; // panic handler 10 | 11 | use cortex_m_rt::entry; 12 | use stm32f7xx_hal as hal; 13 | 14 | use crate::hal::{pac, prelude::*}; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | if let (Some(dp), Some(cp)) = ( 19 | pac::Peripherals::take(), 20 | cortex_m::peripheral::Peripherals::take(), 21 | ) { 22 | // Set up the LED. On the Nucleo-446RE it's connected to pin PA5. 23 | let gpioa = dp.GPIOA.split(); 24 | let mut led = gpioa.pa5.into_push_pull_output(); 25 | 26 | // Set up the system clock. We want to run at 48MHz for this one. 27 | let rcc = dp.RCC.constrain(); 28 | let clocks = rcc.cfgr.sysclk(48.MHz()).freeze(); 29 | 30 | // Create a delay abstraction based on SysTick 31 | let mut delay = cp.SYST.delay(&clocks); 32 | 33 | loop { 34 | // On for 1s, off for 1s. 35 | led.set_high(); 36 | delay.delay_ms(1000_u32); 37 | led.set_low(); 38 | delay.delay_ms(1000_u32); 39 | } 40 | } 41 | 42 | loop {} 43 | } 44 | -------------------------------------------------------------------------------- /examples/delay-timer-blinky.rs: -------------------------------------------------------------------------------- 1 | //! Demonstrate the use of a blocking `Delay` using TIM5 general-purpose timer. 2 | 3 | #![deny(unsafe_code)] 4 | #![allow(clippy::empty_loop)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | // Halt on panic 9 | use panic_halt as _; // panic handler 10 | 11 | use cortex_m_rt::entry; 12 | use stm32f7xx_hal as hal; 13 | 14 | use crate::hal::{ 15 | pac, 16 | prelude::*, 17 | rcc::{HSEClock, HSEClockMode}, 18 | }; 19 | 20 | #[entry] 21 | fn main() -> ! { 22 | if let (Some(dp), Some(_cp)) = ( 23 | pac::Peripherals::take(), 24 | cortex_m::peripheral::Peripherals::take(), 25 | ) { 26 | // Set up the LED. On the Mini-F4 it's connected to pin PC13. 27 | let gpioc = dp.GPIOC.split(); 28 | let mut led = gpioc.pc13.into_push_pull_output(); 29 | 30 | // Set up the system clock. We want to run at 48MHz for this one. 31 | let rcc = dp.RCC.constrain(); 32 | let clocks = rcc 33 | .cfgr 34 | .hse(HSEClock::new(25.MHz(), HSEClockMode::Bypass)) 35 | .sysclk(48.MHz()) 36 | .freeze(); 37 | 38 | // Create a delay abstraction based on general-pupose 32-bit timer TIM5 39 | let mut delay = dp.TIM5.delay_us(&clocks); 40 | 41 | loop { 42 | // On for 1s, off for 3s. 43 | led.set_high(); 44 | // Use `embedded_hal::DelayMs` trait 45 | delay.delay_ms(1000_u32); 46 | led.set_low(); 47 | // or use `fugit::ExtU32` trait 48 | delay.delay(3.secs()); 49 | } 50 | } 51 | 52 | loop {} 53 | } 54 | -------------------------------------------------------------------------------- /examples/exti_button.rs: -------------------------------------------------------------------------------- 1 | //! This example demonstrates how to use the ExtiPin trait for a GPIO input pin to capture a 2 | //! push button press. This example was written for the Nucleo-F767ZI board from the Nucleo-144 3 | //! family of boards, targeting the STM32F767ZI microcontroller. To port this to another board, 4 | //! change the GPIOs used for the push button and for the debug LED. Note that the EXTI number 5 | //! may change if using a new button, meaning that the interrupt handler will need to change also. 6 | //! 7 | //! The intended behavior of the example is that when the user presses the button, an LED is 8 | //! toggled. 9 | 10 | #![no_main] 11 | #![no_std] 12 | 13 | extern crate panic_halt; 14 | 15 | use core::cell::{Cell, RefCell}; 16 | use cortex_m::interrupt::{free, Mutex}; 17 | use cortex_m::peripheral::NVIC; 18 | use cortex_m_rt::entry; 19 | use stm32f7xx_hal::gpio::gpioc::PC13; 20 | use stm32f7xx_hal::gpio::{Edge, ExtiPin, Floating, Input}; 21 | use stm32f7xx_hal::{interrupt, pac, prelude::*}; 22 | 23 | // Semaphore for synchronization 24 | static SEMAPHORE: Mutex> = Mutex::new(Cell::new(true)); 25 | 26 | // GPIO pin that main thread and interrupt handler must share 27 | static BUTTON_PIN: Mutex>>>> = Mutex::new(RefCell::new(None)); 28 | 29 | #[entry] 30 | fn main() -> ! { 31 | let pac_periph = pac::Peripherals::take().unwrap(); 32 | 33 | // Debug LED configuration 34 | let gpiob = pac_periph.GPIOB.split(); 35 | let mut led1 = gpiob.pb0.into_push_pull_output(); 36 | 37 | // Freeze clocks 38 | let mut rcc = pac_periph.RCC.constrain(); 39 | let _clocks = rcc.cfgr.sysclk(216_000_000.Hz()).freeze(); 40 | 41 | // Push button configuration 42 | let mut syscfg = pac_periph.SYSCFG; 43 | let mut exti = pac_periph.EXTI; 44 | let gpioc = pac_periph.GPIOC.split(); 45 | let mut button = gpioc.pc13.into_floating_input(); 46 | button.make_interrupt_source(&mut syscfg, &mut rcc.apb2); 47 | button.trigger_on_edge(&mut exti, Edge::Rising); 48 | button.enable_interrupt(&mut exti); 49 | 50 | // Save information needed by the interrupt handler to the global variable 51 | free(|cs| { 52 | BUTTON_PIN.borrow(cs).replace(Some(button)); 53 | }); 54 | 55 | // Enable the button interrupt 56 | unsafe { 57 | NVIC::unmask::(interrupt::EXTI15_10); 58 | } 59 | 60 | loop { 61 | // Wait for the interrupt to fire 62 | free(|cs| { 63 | if SEMAPHORE.borrow(cs).get() == false { 64 | // Toggle debug LED 65 | led1.toggle(); 66 | 67 | SEMAPHORE.borrow(cs).set(true); 68 | } 69 | }); 70 | } 71 | } 72 | 73 | #[interrupt] 74 | fn EXTI15_10() { 75 | free(|cs| { 76 | match BUTTON_PIN.borrow(cs).borrow_mut().as_mut() { 77 | // Clear the push button interrupt 78 | Some(b) => b.clear_interrupt_pending_bit(), 79 | 80 | // This should never happen 81 | None => (), 82 | } 83 | 84 | // Signal that the interrupt fired 85 | SEMAPHORE.borrow(cs).set(false); 86 | }); 87 | } 88 | -------------------------------------------------------------------------------- /examples/flash.rs: -------------------------------------------------------------------------------- 1 | //! Erases a flash sector and programs data. 2 | 3 | #![deny(unsafe_code)] 4 | #![deny(warnings)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | extern crate panic_semihosting; 9 | 10 | use cortex_m_rt::entry; 11 | use cortex_m_semihosting::hprintln; 12 | 13 | use stm32f7xx_hal::{flash::Flash, pac}; 14 | 15 | const DATA: &[u8] = &[0, 1, 2, 3, 4]; 16 | 17 | #[entry] 18 | fn main() -> ! { 19 | let p = pac::Peripherals::take().unwrap(); 20 | 21 | let mut flash = Flash::new(p.FLASH); 22 | 23 | // The flash needs to be unlocked before any erase or program operations. 24 | flash.unlock(); 25 | 26 | // Erase flash sector 3, which is located at address 0x0800C000 27 | flash.blocking_erase_sector(3).unwrap(); 28 | 29 | // Program the DATA slice into the flash memory starting at offset 0xC00 from the 30 | // beginning of the flash memory. 31 | flash.blocking_program(0xC000, &DATA).unwrap(); 32 | 33 | // Lock the flash memory to prevent any accidental modification of the flash content. 34 | flash.lock(); 35 | 36 | // Create a slice that can be used to read the written data. 37 | #[allow(unsafe_code)] 38 | let flash_data = unsafe { core::slice::from_raw_parts(0x0800C000 as *const u8, DATA.len()) }; 39 | 40 | // Compare the written data with the expected value. 41 | if flash_data == DATA { 42 | hprintln!("Flash programming successful"); 43 | } 44 | 45 | loop {} 46 | } 47 | -------------------------------------------------------------------------------- /examples/fmc.rs: -------------------------------------------------------------------------------- 1 | //! This example shows how to use the FMC controller on the STM32F7 to communicate with an 2 | //! off-chip SDRAM memory device. The `stm32-fmc` crate does the majority of the work, and 3 | //! after initialization the SDRAM is memory mapped to the STM32F7 address space. 4 | //! 5 | //! This example was tested on the STM32F746G Discovery Board. The board has an IS42S32400F-6BL 6 | //! SDRAM chip, with only 16 of the 32 data wires connected to the microcontroller. This device 7 | //! is not explictly supported in the `stm32-fmc` crate at time of writing, but the IS42S16400J 8 | //! has very similar parameters and is used as a placeholder for now. Because of this, only half 9 | //! of the available memory space is available (128 Mb = 16 MB, so 8 MB available). 10 | //! 11 | //! To use the example, launch a GDB server and then `cargo run`. After several seconds, the 12 | //! message "Success!" should be printed in the GDB server terminal (via semihosting). 13 | 14 | #![deny(warnings)] 15 | #![no_main] 16 | #![no_std] 17 | 18 | extern crate panic_semihosting; 19 | 20 | use core::{mem, slice}; 21 | use cortex_m_rt::entry; 22 | use cortex_m_semihosting::hprintln; 23 | use stm32_fmc::devices::is42s16400j_7; 24 | use stm32f7xx_hal::{gpio::Speed, pac, prelude::*}; 25 | 26 | /// Configure pins for the FMC controller 27 | macro_rules! fmc_pins { 28 | ($($pin:expr),*) => { 29 | ( 30 | $( 31 | $pin.into_push_pull_output() 32 | .set_speed(Speed::VeryHigh) 33 | .into_alternate() 34 | .internal_pull_up(true) 35 | ),* 36 | ) 37 | }; 38 | } 39 | 40 | #[entry] 41 | fn main() -> ! { 42 | let cp = cortex_m::Peripherals::take().unwrap(); 43 | let dp = pac::Peripherals::take().unwrap(); 44 | 45 | // Get the delay provider. 46 | let clocks = dp.RCC.constrain().cfgr.sysclk(216.MHz()).freeze(); 47 | let mut delay = cp.SYST.delay(&clocks); 48 | 49 | // IO 50 | let gpioc = dp.GPIOC.split(); 51 | let gpiod = dp.GPIOD.split(); 52 | let gpioe = dp.GPIOE.split(); 53 | let gpiof = dp.GPIOF.split(); 54 | let gpiog = dp.GPIOG.split(); 55 | let gpioh = dp.GPIOH.split(); 56 | 57 | // Initialise SDRAM 58 | let fmc_io = fmc_pins! { 59 | gpiof.pf0, // A0 60 | gpiof.pf1, // A1 61 | gpiof.pf2, // A2 62 | gpiof.pf3, // A3 63 | gpiof.pf4, // A4 64 | gpiof.pf5, // A5 65 | gpiof.pf12, // A6 66 | gpiof.pf13, // A7 67 | gpiof.pf14, // A8 68 | gpiof.pf15, // A9 69 | gpiog.pg0, // A10 70 | gpiog.pg1, // A11 71 | gpiog.pg4, // BA0 72 | gpiog.pg5, // BA1 73 | gpiod.pd14, // D0 74 | gpiod.pd15, // D1 75 | gpiod.pd0, // D2 76 | gpiod.pd1, // D3 77 | gpioe.pe7, // D4 78 | gpioe.pe8, // D5 79 | gpioe.pe9, // D6 80 | gpioe.pe10, // D7 81 | gpioe.pe11, // D8 82 | gpioe.pe12, // D9 83 | gpioe.pe13, // D10 84 | gpioe.pe14, // D11 85 | gpioe.pe15, // D12 86 | gpiod.pd8, // D13 87 | gpiod.pd9, // D14 88 | gpiod.pd10, // D15 89 | gpioe.pe0, // NBL0 90 | gpioe.pe1, // NBL1 91 | gpioc.pc3, // SDCKEn 92 | gpiog.pg8, // SDCLK 93 | gpiog.pg15, // SDNCAS 94 | gpioh.ph3, // SDNEn 95 | gpiof.pf11, // SDNRAS 96 | gpioh.ph5 // SDNWE 97 | }; 98 | 99 | // New SDRAM 100 | let mut sdram = dp.FMC.sdram(fmc_io, is42s16400j_7::Is42s16400j {}, &clocks); 101 | 102 | // Initialise controller and SDRAM 103 | let len_bytes = (16 * 1024 * 1024) / 2; 104 | let len_words = len_bytes / mem::size_of::(); 105 | let ram = unsafe { 106 | let ram_ptr: *mut u32 = sdram.init(&mut delay); 107 | slice::from_raw_parts_mut(ram_ptr, len_words) 108 | }; 109 | 110 | // Access all the words in SDRAM (takes several seconds) 111 | for addr in 0..len_words { 112 | let val: u32 = addr as u32; 113 | 114 | // Write 115 | ram[addr] = val; 116 | 117 | // Read 118 | let res: u32 = ram[addr]; 119 | if res != val { 120 | panic!( 121 | "Error: Expected {} while reading address {:X} but got {}.", 122 | val, addr, res 123 | ); 124 | } 125 | } 126 | 127 | hprintln!("Success!"); 128 | 129 | loop {} 130 | } 131 | -------------------------------------------------------------------------------- /examples/hello.rs: -------------------------------------------------------------------------------- 1 | //! Prints "Hello, world" on the OpenOCD console 2 | 3 | #![deny(unsafe_code)] 4 | #![deny(warnings)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | extern crate panic_semihosting; 9 | extern crate stm32f7xx_hal; 10 | 11 | use cortex_m_rt::entry; 12 | use cortex_m_semihosting::hprintln; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | hprintln!("Hello, world!"); 17 | loop {} 18 | } 19 | -------------------------------------------------------------------------------- /examples/i2c_scanner.rs: -------------------------------------------------------------------------------- 1 | //! Example of using I2C. 2 | //! Scans available I2C devices on bus and print the result. 3 | 4 | #![no_std] 5 | #![no_main] 6 | 7 | use core::ops::Range; 8 | 9 | use panic_semihosting as _; 10 | 11 | use cortex_m_rt::entry; 12 | use cortex_m_semihosting::{hprint, hprintln}; 13 | 14 | use stm32f7xx_hal::{self as hal, gpio::GpioExt, pac, prelude::*}; 15 | 16 | const VALID_ADDR_RANGE: Range = 0x08..0x78; 17 | 18 | #[entry] 19 | fn main() -> ! { 20 | let dp = pac::Peripherals::take().unwrap(); 21 | 22 | let mut rcc = dp.RCC.constrain(); 23 | let clocks = rcc.cfgr.freeze(); 24 | 25 | let gpiob = dp.GPIOB.split(); 26 | 27 | // Configure I2C1 28 | let scl = gpiob.pb8.into_alternate_open_drain::<4>(); 29 | let sda = gpiob.pb7.into_alternate_open_drain::<4>(); 30 | let mut i2c = hal::i2c::BlockingI2c::i2c1( 31 | dp.I2C1, 32 | (scl, sda), 33 | hal::i2c::Mode::fast(100_000.Hz()), 34 | &clocks, 35 | &mut rcc.apb1, 36 | 50_000, 37 | ); 38 | 39 | hprintln!("Start i2c scanning..."); 40 | hprintln!(); 41 | 42 | for addr in 0x00_u8..0x80 { 43 | // Write the empty array and check the slave response. 44 | let byte: [u8; 1] = [0; 1]; 45 | if VALID_ADDR_RANGE.contains(&addr) && i2c.write(addr, &byte).is_ok() { 46 | hprint!("{:02x}", addr); 47 | } else { 48 | hprint!(".."); 49 | } 50 | if addr % 0x10 == 0x0F { 51 | hprintln!(); 52 | } else { 53 | hprint!(" "); 54 | } 55 | } 56 | 57 | hprintln!(); 58 | hprintln!("Done!"); 59 | 60 | loop {} 61 | } 62 | -------------------------------------------------------------------------------- /examples/pwm-sinus.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![no_main] 3 | #![no_std] 4 | 5 | // Halt on panic 6 | use panic_halt as _; 7 | 8 | use core::f32::consts::FRAC_PI_2; 9 | use cortex_m_rt::entry; 10 | use micromath::F32Ext; 11 | use stm32f7xx_hal::{ 12 | pac, 13 | prelude::*, 14 | rcc::{HSEClock, HSEClockMode}, 15 | timer::Channel, 16 | }; 17 | 18 | #[entry] 19 | fn main() -> ! { 20 | if let Some(dp) = pac::Peripherals::take() { 21 | // Set up the system clock. 22 | let rcc = dp.RCC.constrain(); 23 | let clocks = rcc 24 | .cfgr 25 | .hse(HSEClock::new(25.MHz(), HSEClockMode::Bypass)) 26 | .freeze(); 27 | 28 | let gpioa = dp.GPIOA.split(); 29 | let channels = (gpioa.pa8.into_alternate(), gpioa.pa9.into_alternate()); 30 | 31 | let mut pwm = dp.TIM1.pwm_us(channels, 100.micros(), &clocks); 32 | let mut counter = dp.TIM2.counter_us(&clocks); 33 | let max_duty = pwm.get_max_duty(); 34 | 35 | const N: usize = 50; 36 | let mut sin_a = [0_u16; N + 1]; 37 | // Fill sinus array 38 | let a = FRAC_PI_2 / (N as f32); 39 | for (i, b) in sin_a.iter_mut().enumerate() { 40 | let angle = a * (i as f32); 41 | *b = (angle.sin() * (max_duty as f32)) as u16; 42 | } 43 | 44 | counter.start(100.micros()).unwrap(); 45 | pwm.enable(Channel::C1); 46 | pwm.enable(Channel::C2); 47 | let mut i = 0; 48 | loop { 49 | if i == 0 { 50 | pwm.set_duty(Channel::C2, 0); 51 | } 52 | if i == 2 * N { 53 | pwm.set_duty(Channel::C1, 0); 54 | } 55 | if i < N { 56 | pwm.set_duty(Channel::C1, sin_a[i]); 57 | } else if i < 2 * N { 58 | pwm.set_duty(Channel::C1, sin_a[2 * N - i]); 59 | } else if i < 3 * N { 60 | pwm.set_duty(Channel::C2, sin_a[i - 2 * N]); 61 | } else { 62 | pwm.set_duty(Channel::C2, sin_a[4 * N - i]); 63 | } 64 | nb::block!(counter.wait()).unwrap(); 65 | i += 1; 66 | if i == 4 * N { 67 | i -= 4 * N; 68 | } 69 | } 70 | } 71 | 72 | loop { 73 | cortex_m::asm::nop(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /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 | use stm32f7xx_hal::{pac, prelude::*}; 10 | 11 | #[entry] 12 | fn main() -> ! { 13 | if let Some(dp) = pac::Peripherals::take() { 14 | // Set up the system clock. 15 | let rcc = dp.RCC.constrain(); 16 | let clocks = rcc.cfgr.freeze(); 17 | 18 | let gpioa = dp.GPIOA.split(); 19 | let channels = (gpioa.pa8.into_alternate(), gpioa.pa9.into_alternate()); 20 | 21 | let pwm = dp.TIM1.pwm_hz(channels, 20.kHz(), &clocks).split(); 22 | let (mut ch1, _ch2) = pwm; 23 | let max_duty = ch1.get_max_duty(); 24 | ch1.set_duty(max_duty / 2); 25 | ch1.enable(); 26 | } 27 | 28 | loop { 29 | cortex_m::asm::nop(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/rng.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![deny(warnings)] 3 | #![no_main] 4 | #![no_std] 5 | 6 | /// generates a random number and displays it to the openocd console 7 | use panic_semihosting as _; 8 | 9 | use cortex_m_rt::entry; 10 | use cortex_m_semihosting::hprintln; 11 | use stm32f7xx_hal::{pac, prelude::*}; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | let p = pac::Peripherals::take().unwrap(); 16 | let mut rng = p.RNG.init(); 17 | let val = rng.get_rand().unwrap(); 18 | hprintln!("random value {}", val); 19 | loop { 20 | core::hint::spin_loop(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/rtc.rs: -------------------------------------------------------------------------------- 1 | //! This example demonstrates how to use the RTC. 2 | //! Note that the LSI can be quite inaccurate. 3 | //! The tolerance is up to ±47% (Min 17 kHz, Typ 32 kHz, Max 47 kHz). 4 | 5 | #![no_main] 6 | #![no_std] 7 | 8 | extern crate panic_halt as _; 9 | use cortex_m_rt::entry; 10 | 11 | use cortex_m_semihosting::hprintln; 12 | use stm32f7xx_hal::{ 13 | pac, 14 | prelude::*, 15 | rtc::{Rtc, RtcClock}, 16 | }; 17 | use time::{ 18 | macros::{date, time}, 19 | PrimitiveDateTime, 20 | }; 21 | 22 | #[entry] 23 | fn main() -> ! { 24 | let mut p = pac::Peripherals::take().unwrap(); 25 | let mut rcc = p.RCC.constrain(); 26 | 27 | let clocks = rcc.cfgr.lsi().freeze(); 28 | 29 | let mut rtc = Rtc::new( 30 | p.RTC, 31 | 249, 32 | 127, 33 | RtcClock::Lsi, 34 | clocks, 35 | &mut rcc.apb1, 36 | &mut p.PWR, 37 | ) 38 | .unwrap(); 39 | 40 | rtc.set_datetime(&PrimitiveDateTime::new(date!(2019 - 01 - 01), time!(23:59))) 41 | .unwrap(); 42 | // Alternatively: 43 | // rtc.set_date(&date!(2019 - 01 - 01)).unwrap(); 44 | // rtc.set_time(&time!(23:59)).unwrap(); 45 | // Or: 46 | // rtc.set_year(2019).unwrap(); 47 | // rtc.set_month(12).unwrap(); 48 | // rtc.set_day(31).unwrap(); 49 | // rtc.set_hours(23).unwrap(); 50 | // rtc.set_minutes(59).unwrap(); 51 | // rtc.set_seconds(59).unwrap(); 52 | loop { 53 | hprintln!("{}", rtc.get_datetime()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/rtic-tick.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | #[rtic::app(device = stm32f7xx_hal::pac, dispatchers = [USART1])] 7 | mod app { 8 | use stm32f7xx_hal::{ 9 | gpio::{Output, PC13}, 10 | pac, 11 | prelude::*, 12 | timer::MonoTimerUs, 13 | }; 14 | 15 | #[shared] 16 | struct Shared {} 17 | 18 | #[local] 19 | struct Local { 20 | led: PC13, 21 | } 22 | 23 | #[monotonic(binds = TIM2, default = true)] 24 | type MicrosecMono = MonoTimerUs; 25 | 26 | #[init] 27 | fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) { 28 | let rcc = ctx.device.RCC.constrain(); 29 | let clocks = rcc.cfgr.sysclk(48.MHz()).freeze(); 30 | 31 | let gpioc = ctx.device.GPIOC.split(); 32 | let led = gpioc.pc13.into_push_pull_output(); 33 | 34 | let mono = ctx.device.TIM2.monotonic_us(&clocks); 35 | tick::spawn().ok(); 36 | (Shared {}, Local { led }, init::Monotonics(mono)) 37 | } 38 | 39 | #[idle] 40 | fn idle(_: idle::Context) -> ! { 41 | loop {} 42 | } 43 | 44 | #[task(local = [led])] 45 | fn tick(ctx: tick::Context) { 46 | tick::spawn_after(1.secs()).ok(); 47 | ctx.local.led.toggle(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/serial_delay.rs: -------------------------------------------------------------------------------- 1 | //! Write a string to the serial port every half second. 2 | //! 3 | //! Note: This example is for the STM32F745/STM32F746 4 | 5 | #![deny(unsafe_code)] 6 | #![deny(warnings)] 7 | #![no_main] 8 | #![no_std] 9 | 10 | extern crate panic_halt; 11 | 12 | use core::fmt::Write; 13 | 14 | use cortex_m_rt::entry; 15 | use stm32f7xx_hal::{ 16 | pac, 17 | prelude::*, 18 | serial::{self, Serial}, 19 | }; 20 | 21 | #[entry] 22 | fn main() -> ! { 23 | let p = pac::Peripherals::take().unwrap(); 24 | let cp = cortex_m::Peripherals::take().unwrap(); 25 | 26 | let rcc = p.RCC.constrain(); 27 | let clocks = rcc.cfgr.sysclk(216_000_000.Hz()).freeze(); 28 | 29 | let mut delay = cp.SYST.delay(&clocks); 30 | 31 | let gpioa = p.GPIOA.split(); 32 | let gpiob = p.GPIOB.split(); 33 | 34 | let tx = gpioa.pa9.into_alternate(); 35 | let rx = gpiob.pb7.into_alternate(); 36 | 37 | let serial = Serial::new( 38 | p.USART1, 39 | (tx, rx), 40 | &clocks, 41 | serial::Config { 42 | // Default to 115_200 bauds 43 | ..Default::default() 44 | }, 45 | ); 46 | let (mut tx, _) = serial.split(); 47 | 48 | let hello: &str = "Hello, I'm a STM32F7xx!\r\n"; 49 | loop { 50 | tx.write_str(hello).unwrap(); 51 | delay.delay_ms(500_u16); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/serial_dma.rs: -------------------------------------------------------------------------------- 1 | //! Reads 4 bytes from USART then writes them back, both using DMA 2 | //! 3 | //! Echoing 4 bytes at a time makes for weird behavior, but is a better test for 4 | //! DMA than doing it byte by byte would be. 5 | //! 6 | //! Note: This example is for the STM32F746 7 | 8 | #![deny(warnings)] 9 | #![no_main] 10 | #![no_std] 11 | 12 | extern crate panic_halt; 13 | 14 | use core::pin::Pin; 15 | 16 | use cortex_m::{asm, interrupt}; 17 | use cortex_m_rt::entry; 18 | use stm32f7xx_hal::{ 19 | dma::{self, DMA}, 20 | pac, 21 | prelude::*, 22 | serial::{self, Serial}, 23 | }; 24 | 25 | #[entry] 26 | fn main() -> ! { 27 | let p = pac::Peripherals::take().unwrap(); 28 | 29 | let mut rcc = p.RCC.constrain(); 30 | 31 | let dma = DMA::new(p.DMA1); 32 | 33 | let mut rx_stream = dma.streams.stream1; 34 | let mut tx_stream = dma.streams.stream3; 35 | 36 | let dma = dma.handle.enable(&mut rcc.ahb1); 37 | 38 | let clocks = rcc.cfgr.sysclk(216_000_000.Hz()).freeze(); 39 | 40 | let gpiod = p.GPIOD.split(); 41 | 42 | let tx = gpiod.pd8.into_alternate(); 43 | let rx = gpiod.pd9.into_alternate(); 44 | 45 | let serial = Serial::new( 46 | p.USART3, 47 | (tx, rx), 48 | &clocks, 49 | serial::Config { 50 | // Default to 115_200 bauds 51 | ..Default::default() 52 | }, 53 | ); 54 | let (mut tx, mut rx) = serial.split(); 55 | 56 | // Create the buffer we're going to use for DMA. This is safe, as this 57 | // function won't return as long as the program runs, so there's no chance 58 | // of anyone else using the same static. 59 | static mut BUFFER: [u8; 4] = [0; 4]; 60 | let mut buffer = unsafe { Pin::new(&mut *core::ptr::addr_of_mut!(BUFFER)) }; 61 | loop { 62 | // Read using DMA 63 | let mut transfer = rx.read_all(buffer, &dma, rx_stream); 64 | let res = interrupt::free(|_| { 65 | transfer.enable_interrupts( 66 | &dma, 67 | dma::Interrupts { 68 | transfer_complete: true, 69 | transfer_error: true, 70 | direct_mode_error: true, 71 | ..dma::Interrupts::default() 72 | }, 73 | ); 74 | 75 | let transfer = transfer.start(&dma); 76 | 77 | asm::wfi(); 78 | 79 | transfer.wait(&dma).unwrap() 80 | }); 81 | buffer = res.buffer; 82 | rx = res.target; 83 | rx_stream = res.stream; 84 | 85 | // Write using DMA 86 | let mut transfer = tx.write_all(buffer, &dma, tx_stream); 87 | let res = interrupt::free(|_| { 88 | transfer.enable_interrupts( 89 | &dma, 90 | dma::Interrupts { 91 | transfer_complete: true, 92 | transfer_error: true, 93 | direct_mode_error: true, 94 | ..dma::Interrupts::default() 95 | }, 96 | ); 97 | 98 | let transfer = transfer.start(&dma); 99 | 100 | asm::wfi(); 101 | 102 | transfer.wait(&dma).unwrap() 103 | }); 104 | buffer = res.buffer; 105 | tx = res.target; 106 | tx_stream = res.stream; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /examples/serial_echo.rs: -------------------------------------------------------------------------------- 1 | //! Echo characters sent back to the serial port. 2 | //! 3 | //! Note: This example is for the STM32F745/STM32F746 4 | 5 | #![deny(unsafe_code)] 6 | #![deny(warnings)] 7 | #![no_main] 8 | #![no_std] 9 | 10 | extern crate panic_halt; 11 | 12 | use nb::block; 13 | 14 | use cortex_m_rt::entry; 15 | use stm32f7xx_hal::{ 16 | pac, 17 | prelude::*, 18 | serial::{self, Serial}, 19 | }; 20 | 21 | #[entry] 22 | fn main() -> ! { 23 | let p = pac::Peripherals::take().unwrap(); 24 | 25 | let rcc = p.RCC.constrain(); 26 | let clocks = rcc.cfgr.sysclk(216_000_000.Hz()).freeze(); 27 | 28 | let gpioa = p.GPIOA.split(); 29 | let gpiob = p.GPIOB.split(); 30 | 31 | let tx = gpioa.pa9.into_alternate(); 32 | let rx = gpiob.pb7.into_alternate(); 33 | 34 | let serial = Serial::new( 35 | p.USART1, 36 | (tx, rx), 37 | &clocks, 38 | serial::Config { 39 | // Default to 115_200 bauds 40 | ..Default::default() 41 | }, 42 | ); 43 | 44 | let (mut tx, mut rx) = serial.split(); 45 | 46 | loop { 47 | let received = block!(rx.read()).unwrap_or('E' as u8); 48 | block!(tx.write(received)).ok(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/serial_parity.rs: -------------------------------------------------------------------------------- 1 | //! Write characters to the serial port with parity. 2 | //! 3 | //! Note: This example is for the STM32F767 4 | 5 | #![deny(unsafe_code)] 6 | #![deny(warnings)] 7 | #![no_main] 8 | #![no_std] 9 | 10 | extern crate panic_halt; 11 | 12 | use nb::block; 13 | 14 | use cortex_m_rt::entry; 15 | use stm32f7xx_hal::{ 16 | pac, 17 | prelude::*, 18 | serial::{self, Serial, DataBits, Parity}, 19 | }; 20 | 21 | #[entry] 22 | fn main() -> ! { 23 | let p = pac::Peripherals::take().unwrap(); 24 | 25 | let rcc = p.RCC.constrain(); 26 | let clocks = rcc.cfgr.sysclk(48.MHz()).freeze(); 27 | 28 | let mut delay = p.TIM5.delay_ms(&clocks); 29 | 30 | let gpiod = p.GPIOD.split(); 31 | 32 | let tx = gpiod.pd5.into_alternate(); 33 | let rx = gpiod.pd6.into_alternate(); 34 | 35 | let serial = Serial::new( 36 | p.USART2, 37 | (tx, rx), 38 | &clocks, 39 | serial::Config { 40 | // Using 8 bits of data + 1 for even parity 41 | data_bits: DataBits::Bits9, 42 | parity: Parity::ParityEven, 43 | // Default to 115_200 bauds 44 | ..Default::default() 45 | }, 46 | ); 47 | 48 | let (mut tx, mut _rx) = serial.split(); 49 | 50 | let mut byte: u8 = 0; 51 | 52 | loop { 53 | block!(tx.write(byte)).ok(); 54 | 55 | byte = byte.wrapping_add(1); 56 | 57 | delay.delay(10.millis()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/spi.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_semihosting; 5 | 6 | use cortex_m_rt::entry; 7 | use stm32f7xx_hal::{ 8 | pac, 9 | prelude::*, 10 | spi::{self, Spi}, 11 | }; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | let p = pac::Peripherals::take().unwrap(); 16 | 17 | let mut rcc = p.RCC.constrain(); 18 | let clocks = rcc.cfgr.freeze(); 19 | 20 | let gpiob = p.GPIOB.split(); 21 | let gpioc = p.GPIOC.split(); 22 | 23 | // Prepare status LEDS. These happen to be the red and green ones on the 24 | // NUCLEO-F746ZG board. 25 | let mut green = gpiob.pb0.into_push_pull_output(); 26 | let mut red = gpiob.pb14.into_push_pull_output(); 27 | 28 | // Prepare pins for SPI 29 | let mut ncs = gpioc.pc9.into_push_pull_output(); 30 | let sck = gpioc.pc10.into_alternate(); 31 | let miso = gpioc.pc11.into_alternate(); 32 | let mosi = gpioc.pc12.into_alternate(); 33 | 34 | // Set NCS pin to high (disabled) initially 35 | ncs.set_high(); 36 | 37 | // Initialize SPI 38 | let mut spi = Spi::new(p.SPI3, (sck, miso, mosi)).enable::( 39 | spi::Mode { 40 | polarity: spi::Polarity::IdleHigh, 41 | phase: spi::Phase::CaptureOnSecondTransition, 42 | }, 43 | 250.kHz(), 44 | &clocks, 45 | &mut rcc.apb1, 46 | ); 47 | 48 | loop { 49 | // Read WHO_AM_I register of an MPU9250 sensor. 50 | let mut buffer = [0; 2]; 51 | buffer[0] = 0x75 | 0x80; 52 | ncs.set_low(); 53 | spi.transfer(&mut buffer).unwrap(); 54 | ncs.set_high(); 55 | 56 | // The WHO_AM_I register should always return 0x71. 57 | if buffer[1] == 0x71 { 58 | green.set_high(); 59 | red.set_low(); 60 | } else { 61 | red.set_high(); 62 | green.set_low(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /examples/spi_16.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_semihosting; 5 | 6 | use cortex_m_rt::entry; 7 | use stm32f7xx_hal::{ 8 | pac, 9 | prelude::*, 10 | spi::{self, Spi}, 11 | }; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | let p = pac::Peripherals::take().unwrap(); 16 | 17 | let mut rcc = p.RCC.constrain(); 18 | let clocks = rcc.cfgr.freeze(); 19 | 20 | let gpioa = p.GPIOA.split(); 21 | let gpioc = p.GPIOC.split(); 22 | let gpiod = p.GPIOD.split(); 23 | 24 | // Configure pin for button. This happens to be the pin for the USER button 25 | // on the NUCLEO-F746ZG board. 26 | let button = gpioc.pc13.into_floating_input(); 27 | 28 | // Prepare pins for SPI 29 | let mut ncs = gpiod.pd14.into_push_pull_output(); 30 | let sck = gpioa.pa5.into_alternate(); 31 | let mosi = gpioa.pa7.into_alternate(); 32 | 33 | // Set NCS pin to high (disabled) initially 34 | ncs.set_high(); 35 | 36 | // Initialize SPI 37 | let mut spi = Spi::new(p.SPI1, (sck, spi::NoMiso, mosi)).enable::( 38 | embedded_hal::spi::MODE_0, 39 | 250.kHz(), 40 | &clocks, 41 | &mut rcc.apb2, 42 | ); 43 | 44 | // Use a button to control output via the Maxim Integrated MAX5214 DAC. 45 | loop { 46 | let data = if button.is_high() { 0xffff } else { 0x0000 }; 47 | 48 | let word: u16 = (0b01 << 14) | // write-through mode 49 | (data & 0x3fff); // data bits 50 | 51 | ncs.set_low(); 52 | spi.write(&[word]).unwrap(); 53 | ncs.set_high(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/spi_dma.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_semihosting; 5 | 6 | use core::pin::Pin; 7 | 8 | use cortex_m::{asm, interrupt}; 9 | use cortex_m_rt::entry; 10 | use stm32f7xx_hal::{ 11 | dma::{self, DMA}, 12 | pac, 13 | prelude::*, 14 | spi::{self, Spi}, 15 | }; 16 | 17 | #[entry] 18 | fn main() -> ! { 19 | let p = pac::Peripherals::take().unwrap(); 20 | 21 | let mut rcc = p.RCC.constrain(); 22 | let clocks = rcc.cfgr.freeze(); 23 | 24 | let dma = DMA::new(p.DMA1); 25 | let gpiob = p.GPIOB.split(); 26 | let gpioc = p.GPIOC.split(); 27 | 28 | // Prepare status LEDS. These happen to be the red and green ones on the 29 | // NUCLEO-F746ZG board. 30 | let mut green = gpiob.pb0.into_push_pull_output(); 31 | let mut red = gpiob.pb14.into_push_pull_output(); 32 | 33 | // Prepare pins for SPI 34 | let mut ncs = gpioc.pc9.into_push_pull_output(); 35 | let sck = gpioc.pc10.into_alternate(); 36 | let miso = gpioc.pc11.into_alternate(); 37 | let mosi = gpioc.pc12.into_alternate(); 38 | 39 | // Prepare DMA streams 40 | let mut rx_stream = dma.streams.stream0; 41 | let mut tx_stream = dma.streams.stream5; 42 | 43 | let dma = dma.handle.enable(&mut rcc.ahb1); 44 | 45 | // Set NCS pin to high (disabled) initially 46 | ncs.set_high(); 47 | 48 | // Initialize SPI 49 | let mut spi = Spi::new(p.SPI3, (sck, miso, mosi)).enable( 50 | spi::Mode { 51 | polarity: spi::Polarity::IdleHigh, 52 | phase: spi::Phase::CaptureOnSecondTransition, 53 | }, 54 | 250.kHz(), 55 | &clocks, 56 | &mut rcc.apb1, 57 | ); 58 | 59 | // Create the buffer we're going to use for DMA. This is safe, as this 60 | // function won't return as long as the program runs, so there's no chance 61 | // of anyone else using the same static. 62 | static mut BUFFER: [u8; 2] = [0; 2]; 63 | let mut buffer = unsafe { Pin::new(&mut *core::ptr::addr_of_mut!(BUFFER)) }; 64 | 65 | loop { 66 | // Read WHO_AM_I register of an MPU9250 sensor. 67 | // Write address for WHO_AM_I register an an MPU9250 sensor 68 | buffer[0] = 0x75 | 0x80; 69 | 70 | // Prepare DMA transfer 71 | let mut transfer = spi.transfer_all(buffer, &dma, &dma, rx_stream, tx_stream); 72 | 73 | // Start DMA transfer and wait for it to finish 74 | ncs.set_low(); 75 | let res = interrupt::free(|_| { 76 | transfer.enable_interrupts( 77 | &dma, 78 | &dma, 79 | dma::Interrupts { 80 | transfer_complete: true, 81 | transfer_error: true, 82 | direct_mode_error: true, 83 | ..dma::Interrupts::default() 84 | }, 85 | ); 86 | 87 | let transfer = transfer.start(&dma, &dma); 88 | 89 | asm::wfi(); 90 | 91 | transfer.wait(&dma, &dma).unwrap() 92 | }); 93 | ncs.set_high(); 94 | 95 | // Assign everything we've moved to the DMA transfer to the local 96 | // variables it came from, so it's available again in the next loop 97 | // iteration. 98 | buffer = res.buffer; 99 | spi = res.target; 100 | rx_stream = res.rx_stream; 101 | tx_stream = res.tx_stream; 102 | 103 | // The WHO_AM_I register should always return 0x71. 104 | if buffer[1] == 0x71 { 105 | green.set_high(); 106 | red.set_low(); 107 | } else { 108 | red.set_high(); 109 | green.set_low(); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /examples/spi_dma_16.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_semihosting; 5 | 6 | use core::pin::Pin; 7 | 8 | use cortex_m::{asm, interrupt}; 9 | use cortex_m_rt::entry; 10 | use stm32f7xx_hal::{ 11 | dma::{self, DMA}, 12 | pac, 13 | prelude::*, 14 | spi::{self, Spi}, 15 | }; 16 | 17 | #[entry] 18 | fn main() -> ! { 19 | let p = pac::Peripherals::take().unwrap(); 20 | 21 | let mut rcc = p.RCC.constrain(); 22 | let clocks = rcc.cfgr.freeze(); 23 | 24 | let dma = DMA::new(p.DMA2); 25 | let gpioa = p.GPIOA.split(); 26 | let gpioc = p.GPIOC.split(); 27 | let gpiod = p.GPIOD.split(); 28 | 29 | // Configure pin for button. This happens to be the pin for the USER button 30 | // on the NUCLEO-F746ZG board. 31 | let button = gpioc.pc13.into_floating_input(); 32 | 33 | // Prepare pins for SPI 34 | let mut ncs = gpiod.pd14.into_push_pull_output(); 35 | let sck = gpioa.pa5.into_alternate(); 36 | let mosi = gpioa.pa7.into_alternate(); 37 | 38 | // Prepare DMA streams 39 | let mut rx_stream = dma.streams.stream0; 40 | let mut tx_stream = dma.streams.stream3; 41 | 42 | let dma = dma.handle.enable(&mut rcc.ahb1); 43 | 44 | // Set NCS pin to high (disabled) initially 45 | ncs.set_high(); 46 | 47 | // Initialize SPI 48 | let mut spi = Spi::new(p.SPI1, (sck, spi::NoMiso, mosi)).enable( 49 | embedded_hal::spi::MODE_0, 50 | 250.kHz(), 51 | &clocks, 52 | &mut rcc.apb2, 53 | ); 54 | 55 | // Create the buffer we're going to use for DMA. This is safe, as this 56 | // function won't return as long as the program runs, so there's no chance 57 | // of anyone else using the same static. 58 | static mut BUFFER: [u16; 1] = [0; 1]; 59 | let mut buffer = unsafe { Pin::new(&mut *core::ptr::addr_of_mut!(BUFFER)) }; 60 | 61 | // Use a button to control output via the Maxim Integrated MAX5214 DAC. 62 | loop { 63 | let data = if button.is_high() { 0xffff } else { 0x0000 }; 64 | 65 | buffer[0] = (0b01 << 14) | // write-through mode 66 | (data & 0x3fff); // data bits 67 | 68 | // Prepare DMA transfer 69 | let mut transfer = spi.transfer_all(buffer, &dma, &dma, rx_stream, tx_stream); 70 | 71 | // Start DMA transfer and wait for it to finish 72 | ncs.set_low(); 73 | let res = interrupt::free(|_| { 74 | transfer.enable_interrupts( 75 | &dma, 76 | &dma, 77 | dma::Interrupts { 78 | transfer_complete: true, 79 | transfer_error: true, 80 | direct_mode_error: true, 81 | ..dma::Interrupts::default() 82 | }, 83 | ); 84 | 85 | let transfer = transfer.start(&dma, &dma); 86 | 87 | asm::wfi(); 88 | 89 | transfer.wait(&dma, &dma).unwrap() 90 | }); 91 | ncs.set_high(); 92 | 93 | buffer = res.buffer; 94 | spi = res.target; 95 | rx_stream = res.rx_stream; 96 | tx_stream = res.tx_stream; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /examples/stm32f7disco-qspi-flash/main.rs: -------------------------------------------------------------------------------- 1 | //! Example code showing how to use the MT25QL128ABA on the STM32F746G Discovery Board. 2 | //! The intended behavior of the example is to write a known pattern to flash memory and 3 | //! read it back using both polling and DMA indirect modes of the QSPI HAL driver. 4 | //! The example will panic on failure and print messages over the debugger on success. 5 | //! See `mt25q.rs` for more details on the QSPI HAL driver. 6 | 7 | #![no_main] 8 | #![no_std] 9 | 10 | extern crate panic_semihosting; 11 | 12 | use core::pin::Pin; 13 | use cortex_m_rt::entry; 14 | use cortex_m_semihosting::hprintln; 15 | use stm32f7xx_hal::{ 16 | dma::{Handle, Stream7, DMA}, 17 | pac::{self, DMA2}, 18 | prelude::*, 19 | rcc::{HSEClock, HSEClockMode, RccExt}, 20 | state, 21 | }; 22 | 23 | mod mt25q; 24 | 25 | #[entry] 26 | fn main() -> ! { 27 | let pac_periph = pac::Peripherals::take().unwrap(); 28 | let mut rcc = pac_periph.RCC; 29 | 30 | // Initialize flash driver, which will initialize QSPI driver 31 | let mut mt25q = mt25q::Mt25q::new( 32 | &mut rcc, 33 | pac_periph.GPIOB, 34 | pac_periph.GPIOD, 35 | pac_periph.GPIOE, 36 | pac_periph.QUADSPI, 37 | ); 38 | 39 | // Init clocks 40 | let hse_cfg = HSEClock::new(25_000_000.Hz(), HSEClockMode::Oscillator); 41 | let mut rcc = rcc.constrain(); 42 | 43 | // Setup DMA 44 | let dma = DMA::new(pac_periph.DMA2); 45 | let stream = dma.streams.stream7; 46 | let dma = dma.handle.enable(&mut rcc.ahb1); 47 | 48 | // Ramp up clocks to 216 MHz 49 | rcc.cfgr.hse(hse_cfg).sysclk(216_000_000.Hz()).freeze(); 50 | 51 | // Check that we can communicate with the flash device 52 | mt25q.check_id(); 53 | 54 | memory_example_polling(&mut mt25q); 55 | memory_example_dma(&mut mt25q, &dma, stream); 56 | 57 | loop {} 58 | } 59 | 60 | fn memory_example_polling(mt25q: &mut mt25q::Mt25q) { 61 | // Create a set of buffers in RAM that will mirror flash memory at address `ADDR` of size `LEN` 62 | const ADDR: u32 = 0x7003; 63 | const LEN: usize = 1035; 64 | let mut read_buffer: [u8; LEN] = [0; LEN]; 65 | let mut write_buffer: [u8; LEN] = [0; LEN]; 66 | for i in 0..LEN { 67 | write_buffer[i] = i as u8; 68 | } 69 | 70 | /////////////////////// 71 | // Test erase + read // 72 | /////////////////////// 73 | 74 | let (num_erase, addr_erase) = mt25q.erase(ADDR, LEN); 75 | assert!(LEN <= num_erase as usize); 76 | assert!(addr_erase <= ADDR); 77 | 78 | mt25q.read(&mut read_buffer, ADDR, LEN); 79 | for i in 0..LEN { 80 | assert!(read_buffer[i] == 0xFF); 81 | } 82 | 83 | /////////////////////// 84 | // Test write + read // 85 | /////////////////////// 86 | 87 | mt25q.write(ADDR, &mut write_buffer, LEN); 88 | mt25q.read(&mut read_buffer, ADDR, LEN); 89 | for i in 0..LEN { 90 | if write_buffer[i] != read_buffer[i] { 91 | panic!( 92 | "Error: Mismatch at address {:X}. Expected {:X} but read {:X}", 93 | ADDR + i as u32, 94 | write_buffer[i], 95 | read_buffer[i] 96 | ); 97 | } 98 | } 99 | 100 | hprintln!("Flash device memory test successful!"); 101 | } 102 | 103 | fn memory_example_dma( 104 | mt25q: &mut mt25q::Mt25q, 105 | dma: &Handle, 106 | stream: Stream7, 107 | ) { 108 | // Create a buffer in RAM that will mirror flash memory at address `ADDR` of size `LEN` 109 | const ADDR: u32 = 0x7000; 110 | const LEN: usize = mt25q::SUBSECTOR_SIZE as usize; 111 | static mut READ_BUFFER: [u8; LEN] = [0; LEN]; 112 | 113 | // Temporary working memory 114 | const PAGE_SIZE: usize = mt25q::PAGE_SIZE as usize; 115 | static mut PAGE_BUFFER: [u8; PAGE_SIZE] = [0; PAGE_SIZE]; 116 | 117 | // Set the page buffer to some random data 118 | unsafe { 119 | for i in 0..PAGE_SIZE { 120 | PAGE_BUFFER[i] = i as u8; 121 | } 122 | } 123 | 124 | // Create pinned versions for DMA transfers 125 | let mut stream = stream; 126 | let mut read_buffer = unsafe { Pin::new(&mut *core::ptr::addr_of_mut!(READ_BUFFER)) }; 127 | let mut page_buffer = unsafe { Pin::new(&mut *core::ptr::addr_of_mut!(PAGE_BUFFER)) }; 128 | 129 | /////////////////////// 130 | // Test erase + read // 131 | /////////////////////// 132 | 133 | let (num_erase, addr_erase) = mt25q.erase(ADDR, LEN); 134 | assert!(LEN <= num_erase as usize); 135 | assert!(addr_erase <= ADDR); 136 | 137 | let read_resources = mt25q.read_dma(read_buffer, ADDR, LEN, dma, stream); 138 | for i in 0..LEN { 139 | assert!(read_resources.buffer[i] == 0xFF); 140 | } 141 | 142 | stream = read_resources.stream; 143 | read_buffer = read_resources.buffer; 144 | 145 | /////////////////////// 146 | // Test write + read // 147 | /////////////////////// 148 | 149 | // For writing with DMA, caller must break the writes down into flash memory pages. 150 | // Note that since ADDR is page aligned and LEN is a multiple of the page size some 151 | // page boundry math is ignored here. See the polling write function in the `mt25q` 152 | // driver for a more advanced example dealing with unaligned page boundries. 153 | let mut curr_addr: u32 = ADDR; 154 | let mut bytes_written: usize = 0; 155 | while bytes_written < LEN { 156 | let write_resources = mt25q.write_page_dma(curr_addr, page_buffer, PAGE_SIZE, dma, stream); 157 | stream = write_resources.stream; 158 | page_buffer = write_resources.buffer; 159 | bytes_written += PAGE_SIZE; 160 | curr_addr += PAGE_SIZE as u32; 161 | } 162 | 163 | mt25q.read_dma(read_buffer, ADDR, LEN, dma, stream); 164 | for i in 0..LEN { 165 | unsafe { 166 | if PAGE_BUFFER[i % PAGE_SIZE] != READ_BUFFER[i] { 167 | panic!( 168 | "Error: Mismatch at address {:X}. Expected {:X} but read {:X}", 169 | ADDR + i as u32, 170 | PAGE_BUFFER[i % PAGE_SIZE], 171 | READ_BUFFER[i] 172 | ); 173 | } 174 | } 175 | } 176 | 177 | hprintln!("Flash device memory DMA test successful!"); 178 | } 179 | -------------------------------------------------------------------------------- /examples/stm32f7disco-screen/main.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | #![no_main] 3 | #![no_std] 4 | 5 | // Required 6 | extern crate panic_semihosting; 7 | 8 | use cortex_m_rt::entry; 9 | use embedded_graphics::{ 10 | egcircle, egrectangle, egtext, 11 | fonts::Font6x8, 12 | pixelcolor::{Rgb565, RgbColor}, 13 | prelude::*, 14 | primitive_style, text_style, 15 | }; 16 | 17 | use stm32f7xx_hal::{ 18 | gpio::Speed, 19 | ltdc::{Layer, PixelFormat}, 20 | pac, 21 | prelude::*, 22 | rcc::{HSEClock, HSEClockMode, Rcc}, 23 | }; 24 | 25 | mod screen; 26 | 27 | // DIMENSIONS 28 | const WIDTH: u16 = 480; 29 | const HEIGHT: u16 = 272; 30 | 31 | // Graphics framebuffer 32 | const FB_GRAPHICS_SIZE: usize = (WIDTH as usize) * (HEIGHT as usize); 33 | static mut FB_LAYER1: [u16; FB_GRAPHICS_SIZE] = [0; FB_GRAPHICS_SIZE]; 34 | 35 | #[entry] 36 | fn main() -> ! { 37 | let perif = pac::Peripherals::take().unwrap(); 38 | let _cp = cortex_m::Peripherals::take().unwrap(); 39 | 40 | let rcc_hal: Rcc = perif.RCC.constrain(); 41 | 42 | // Set up pins 43 | let _gpioa = perif.GPIOA.split(); 44 | let _gpiob = perif.GPIOB.split(); 45 | let gpioe = perif.GPIOE.split(); 46 | let gpiog = perif.GPIOG.split(); 47 | let gpioh = perif.GPIOH.split(); 48 | let gpioi = perif.GPIOI.split(); 49 | let gpioj = perif.GPIOJ.split(); 50 | let gpiok = perif.GPIOK.split(); 51 | 52 | gpioe.pe4.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_B0 53 | 54 | gpiog.pg12.into_alternate::<9>().set_speed(Speed::VeryHigh); // LTCD_B4 55 | 56 | gpioi.pi9.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_VSYNC 57 | gpioi.pi10.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_HSYNC 58 | gpioi.pi13.into_alternate::<14>().set_speed(Speed::VeryHigh); 59 | gpioi.pi14.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_CLK 60 | gpioi.pi15.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_R0 61 | 62 | gpioj.pj0.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_R1 63 | gpioj.pj1.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_R2 64 | gpioj.pj2.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_R3 65 | gpioj.pj3.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_R4 66 | gpioj.pj4.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_R5 67 | gpioj.pj5.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_R6 68 | gpioj.pj6.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_R7 69 | gpioj.pj7.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_G0 70 | gpioj.pj8.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_G1 71 | gpioj.pj9.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_G2 72 | gpioj.pj10.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_G3 73 | gpioj.pj11.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_G4 74 | gpioj.pj13.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_B1 75 | gpioj.pj14.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_B2 76 | gpioj.pj15.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_B3 77 | 78 | gpiok.pk0.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_G5 79 | gpiok.pk1.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_G6 80 | gpiok.pk2.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_G7 81 | gpiok.pk4.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_B5 82 | gpiok.pk5.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_B6 83 | gpiok.pk6.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_D7 84 | gpiok.pk7.into_alternate::<14>().set_speed(Speed::VeryHigh); // LTCD_E 85 | 86 | // HSE osc out in High Z 87 | gpioh.ph1.into_floating_input(); 88 | let _clocks = rcc_hal 89 | .cfgr 90 | .hse(HSEClock::new(25_000_000.Hz(), HSEClockMode::Bypass)) 91 | .sysclk(216_000_000.Hz()) 92 | .hclk(216_000_000.Hz()) 93 | .freeze(); 94 | 95 | // LCD enable: set it low first to avoid LCD bleed while setting up timings 96 | let mut disp_on = gpioi.pi12.into_push_pull_output(); 97 | disp_on.set_low(); 98 | 99 | // LCD backlight enable 100 | let mut backlight = gpiok.pk3.into_push_pull_output(); 101 | backlight.set_high(); 102 | 103 | let mut display = screen::Stm32F7DiscoDisplay::new(perif.LTDC, perif.DMA2D); 104 | display 105 | .controller 106 | .config_layer(Layer::L1, unsafe { &mut *core::ptr::addr_of_mut!(FB_LAYER1) }, PixelFormat::RGB565); 107 | 108 | display.controller.enable_layer(Layer::L1); 109 | display.controller.reload(); 110 | 111 | let display = &mut display; 112 | 113 | // LCD enable: activate LCD ! 114 | disp_on.set_high(); 115 | 116 | let r = egrectangle!( 117 | top_left = (0, 0), 118 | bottom_right = (479, 271), 119 | style = primitive_style!(fill_color = Rgb565::new(0, 0b11110, 0b11011)) 120 | ); 121 | r.draw(display).ok(); 122 | 123 | let c1 = egcircle!( 124 | center = (20, 20), 125 | radius = 8, 126 | style = primitive_style!(fill_color = Rgb565::new(0, 63, 0)) 127 | ); 128 | 129 | let c2 = egcircle!( 130 | center = (25, 20), 131 | radius = 8, 132 | style = primitive_style!(fill_color = Rgb565::new(31, 0, 0)) 133 | ); 134 | 135 | let t = egtext!( 136 | text = "Hello Rust!", 137 | top_left = (100, 100), 138 | style = text_style!(font = Font6x8, text_color = RgbColor::WHITE) 139 | ); 140 | 141 | c1.draw(display).ok(); 142 | c2.draw(display).ok(); 143 | t.draw(display).ok(); 144 | 145 | for i in 0..300 { 146 | let c1 = egcircle!( 147 | center = (20 + i, 20), 148 | radius = 8, 149 | style = primitive_style!(fill_color = RgbColor::GREEN) 150 | ); 151 | c1.draw(display).ok(); 152 | } 153 | 154 | loop {} 155 | } 156 | -------------------------------------------------------------------------------- /examples/stm32f7disco-screen/screen.rs: -------------------------------------------------------------------------------- 1 | use embedded_graphics::{ 2 | drawable::Pixel, 3 | geometry::Size, 4 | pixelcolor::{Rgb565, RgbColor}, 5 | primitives, 6 | style::{PrimitiveStyle, Styled}, 7 | DrawTarget, 8 | }; 9 | 10 | use stm32f7xx_hal::{ 11 | ltdc::{DisplayConfig, DisplayController, Layer, PixelFormat, SupportedWord}, 12 | pac::{DMA2D, LTDC}, 13 | prelude::*, 14 | rcc::{HSEClock, HSEClockMode}, 15 | }; 16 | 17 | /// STM32F7-DISCO board display 18 | pub const DISCO_SCREEN_CONFIG: DisplayConfig = DisplayConfig { 19 | active_width: 480, 20 | active_height: 272, 21 | h_back_porch: 13, 22 | h_front_porch: 30, 23 | h_sync: 41, 24 | v_back_porch: 2, 25 | v_front_porch: 2, 26 | v_sync: 10, 27 | frame_rate: 60, 28 | h_sync_pol: false, 29 | v_sync_pol: false, 30 | no_data_enable_pol: false, 31 | pixel_clock_pol: false, 32 | }; 33 | 34 | pub struct Stm32F7DiscoDisplay { 35 | pub controller: DisplayController, 36 | } 37 | 38 | impl Stm32F7DiscoDisplay { 39 | pub fn new(ltdc: LTDC, dma2d: DMA2D) -> Stm32F7DiscoDisplay { 40 | let controller = DisplayController::new( 41 | ltdc, 42 | dma2d, 43 | PixelFormat::RGB565, 44 | DISCO_SCREEN_CONFIG, 45 | Some(&HSEClock::new(25_000_000.Hz(), HSEClockMode::Bypass)), 46 | ); 47 | 48 | Stm32F7DiscoDisplay { controller } 49 | } 50 | } 51 | 52 | impl DrawTarget for Stm32F7DiscoDisplay { 53 | type Error = core::convert::Infallible; 54 | 55 | /// Draw a `Pixel` that has a color defined 56 | fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Self::Error> { 57 | let Pixel(coord, color) = pixel; 58 | let value: u16 = (color.b() as u16 & 0x1F) 59 | | ((color.g() as u16 & 0x3F) << 5) 60 | | ((color.r() as u16 & 0x1F) << 11); 61 | 62 | // TODO : draw pixel 63 | self.controller 64 | .draw_pixel(Layer::L1, coord.x as usize, coord.y as usize, value); 65 | Ok(()) 66 | } 67 | 68 | /// Draw a hardware accelerated (by DMA2D) rectangle 69 | fn draw_rectangle( 70 | &mut self, 71 | item: &Styled>, 72 | ) -> Result<(), Self::Error> { 73 | if item.style.stroke_color.is_none() { 74 | let top_left = ( 75 | item.primitive.top_left.x as usize, 76 | item.primitive.top_left.y as usize, 77 | ); 78 | let bottom_right = ( 79 | item.primitive.bottom_right.x as usize, 80 | item.primitive.bottom_right.y as usize, 81 | ); 82 | let color = match item.style.fill_color { 83 | Some(c) => { 84 | (c.b() as u32 & 0x1F) 85 | | ((c.g() as u32 & 0x3F) << 5) 86 | | ((c.r() as u32 & 0x1F) << 11) 87 | } 88 | None => 0u32, 89 | }; 90 | 91 | // Note(unsafe) because transfert might not be before an other write 92 | // to the buffer occurs. However, such Register -> Buffer transfert 93 | // is so fast that such issue does not occur 94 | // TODO : use safer DMA api when the embedde-hal DMA traits will be stabilised 95 | unsafe { 96 | self.controller 97 | .draw_rectangle(Layer::L1, top_left, bottom_right, color); 98 | } 99 | } else { 100 | self.draw_iter(item).unwrap(); 101 | } 102 | 103 | Ok(()) 104 | } 105 | 106 | /// Return the size of the screen 107 | fn size(&self) -> Size { 108 | Size::new(480, 272) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /examples/timer-periph.rs: -------------------------------------------------------------------------------- 1 | //! Start and stop a periodic peripheral timer. 2 | //! 3 | //! This example should run on all stm32f4xx boards but it was tested with 4 | //! stm32f4-discovery board (model STM32F407G-DISC1). 5 | //! 6 | //! ```bash 7 | //! cargo run --release --features stm32f407,rt --example timer-periph 8 | //! ``` 9 | 10 | #![no_std] 11 | #![no_main] 12 | 13 | use panic_halt as _; 14 | 15 | use cortex_m_rt::entry; 16 | use cortex_m_semihosting::hprintln; 17 | 18 | use hal::timer::Error; 19 | use stm32f7xx_hal as hal; 20 | 21 | use crate::hal::{pac, prelude::*}; 22 | 23 | #[entry] 24 | fn main() -> ! { 25 | let dp = pac::Peripherals::take().unwrap(); 26 | let rcc = dp.RCC.constrain(); 27 | let clocks = rcc.cfgr.sysclk(24.MHz()).freeze(); 28 | 29 | // Create a timer based on SysTick 30 | let mut timer = dp.TIM1.counter_ms(&clocks); 31 | timer.start(1.secs()).unwrap(); 32 | 33 | hprintln!("hello!"); 34 | // wait until timer expires 35 | nb::block!(timer.wait()).unwrap(); 36 | hprintln!("timer expired 1"); 37 | 38 | // the function counter_ms() creates a periodic timer, so it is automatically 39 | // restarted 40 | nb::block!(timer.wait()).unwrap(); 41 | hprintln!("timer expired 2"); 42 | 43 | // cancel current timer 44 | timer.cancel().unwrap(); 45 | 46 | // start it again 47 | timer.start(1.secs()).unwrap(); 48 | nb::block!(timer.wait()).unwrap(); 49 | hprintln!("timer expired 3"); 50 | 51 | timer.cancel().unwrap(); 52 | let cancel_outcome = timer.cancel(); 53 | assert_eq!(cancel_outcome, Err(Error::Disabled)); 54 | hprintln!("ehy, you cannot cancel a timer two times!"); 55 | // this time the timer was not restarted, therefore this function should 56 | // wait forever 57 | nb::block!(timer.wait()).unwrap(); 58 | // you should never see this print 59 | hprintln!("if you see this there is something wrong"); 60 | panic!(); 61 | } 62 | -------------------------------------------------------------------------------- /examples/timer-syst.rs: -------------------------------------------------------------------------------- 1 | //! Start and stop a periodic system timer. 2 | //! 3 | //! This example should run on all stm32f4xx boards but it was tested with 4 | //! stm32f4-discovery board (model STM32F407G-DISC1). 5 | //! 6 | //! ```bash 7 | //! cargo run --release --features stm32f407,rt --example timer-syst 8 | //! ``` 9 | 10 | #![no_std] 11 | #![no_main] 12 | 13 | use panic_halt as _; 14 | 15 | use cortex_m_rt::entry; 16 | use cortex_m_semihosting::hprintln; 17 | 18 | use hal::timer::Error; 19 | use stm32f7xx_hal as hal; 20 | 21 | use crate::hal::{pac, prelude::*}; 22 | 23 | #[entry] 24 | fn main() -> ! { 25 | let dp = pac::Peripherals::take().unwrap(); 26 | let cp = cortex_m::peripheral::Peripherals::take().unwrap(); 27 | let rcc = dp.RCC.constrain(); 28 | let clocks = rcc.cfgr.sysclk(24.MHz()).freeze(); 29 | 30 | // Create a timer based on SysTick 31 | let mut timer = cp.SYST.counter_us(&clocks); 32 | timer.start(42.millis()).unwrap(); 33 | 34 | hprintln!("hello!"); 35 | // wait until timer expires 36 | nb::block!(timer.wait()).unwrap(); 37 | hprintln!("timer expired 1"); 38 | 39 | // the function syst() creates a periodic timer, so it is automatically 40 | // restarted 41 | nb::block!(timer.wait()).unwrap(); 42 | hprintln!("timer expired 2"); 43 | 44 | // cancel current timer 45 | timer.cancel().unwrap(); 46 | 47 | // start it again 48 | timer.start(42.millis()).unwrap(); 49 | nb::block!(timer.wait()).unwrap(); 50 | hprintln!("timer expired 3"); 51 | 52 | timer.cancel().unwrap(); 53 | let cancel_outcome = timer.cancel(); 54 | assert_eq!(cancel_outcome, Err(Error::Disabled)); 55 | hprintln!("ehy, you cannot cancel a timer two times!"); 56 | // this time the timer was not restarted, therefore this function should 57 | // wait forever 58 | nb::block!(timer.wait()).unwrap(); 59 | // you should never see this print 60 | hprintln!("if you see this there is something wrong"); 61 | panic!(); 62 | } 63 | -------------------------------------------------------------------------------- /examples/usb_serial.rs: -------------------------------------------------------------------------------- 1 | //! CDC-ACM serial port example using polling in a busy loop. 2 | //! Target board: any STM32F7 with an OTG FS/HS peripheral and a 25MHz HSE generator 3 | //! This example works on the 32F723EDISCOVERY board. 4 | //! 5 | //! For FS operation: 6 | //! > cargo run --example usb_serial --features "stm32f723, rt, usb_fs" --release 7 | //! 8 | //! For HS operation: 9 | //! > cargo run --example usb_serial --features "stm32f723, rt, usb_hs" --release 10 | //! 11 | //! Note that `usbd-serial` library used in this example doesn't support 12 | //! HighSpeed mode properly at the moment. See 13 | //! https://github.com/mvirkkunen/usbd-serial/pull/14 for a potential workaround. 14 | #![no_std] 15 | #![no_main] 16 | 17 | use panic_semihosting as _; 18 | 19 | use cortex_m_rt::entry; 20 | #[cfg(feature = "usb_fs")] 21 | use stm32f7xx_hal::otg_fs::{UsbBus, USB}; 22 | #[cfg(feature = "usb_hs")] 23 | use stm32f7xx_hal::otg_hs::{UsbBus, USB}; 24 | use stm32f7xx_hal::pac; 25 | use stm32f7xx_hal::prelude::*; 26 | use stm32f7xx_hal::rcc::{HSEClock, HSEClockMode, PLL48CLK}; 27 | use usb_device::prelude::*; 28 | 29 | #[entry] 30 | fn main() -> ! { 31 | let dp = pac::Peripherals::take().unwrap(); 32 | 33 | let rcc = dp.RCC.constrain(); 34 | 35 | let clocks = rcc 36 | .cfgr 37 | .hse(HSEClock::new(25_000_000.Hz(), HSEClockMode::Bypass)) 38 | .use_pll() 39 | .use_pll48clk(PLL48CLK::Pllq) 40 | .sysclk(216_000_000.Hz()) 41 | .freeze(); 42 | 43 | #[cfg(feature = "usb_fs")] 44 | let gpioa = dp.GPIOA.split(); 45 | #[cfg(feature = "usb_hs")] 46 | let gpiob = dp.GPIOB.split(); 47 | 48 | #[cfg(feature = "usb_fs")] 49 | let usb = USB::new( 50 | dp.OTG_FS_GLOBAL, 51 | dp.OTG_FS_DEVICE, 52 | dp.OTG_FS_PWRCLK, 53 | (gpioa.pa11.into_alternate(), gpioa.pa12.into_alternate()), 54 | &clocks, 55 | ); 56 | #[cfg(all(feature = "usb_hs", not(feature = "usb_hs_phy")))] 57 | let usb = USB::new( 58 | dp.OTG_HS_GLOBAL, 59 | dp.OTG_HS_DEVICE, 60 | dp.OTG_HS_PWRCLK, 61 | (gpiob.pb14.into_alternate(), gpiob.pb15.into_alternate()), 62 | &clocks, 63 | ); 64 | #[cfg(all(feature = "usb_hs", feature = "usb_hs_phy"))] 65 | let usb = USB::new_with_internal_hs_phy( 66 | dp.OTG_HS_GLOBAL, 67 | dp.OTG_HS_DEVICE, 68 | dp.OTG_HS_PWRCLK, 69 | dp.USBPHYC, 70 | (gpiob.pb14.into_alternate(), gpiob.pb15.into_alternate()), 71 | &clocks, 72 | ); 73 | 74 | static mut EP_MEMORY: [u32; 1024] = [0; 1024]; 75 | let usb_bus = UsbBus::new(usb, unsafe { &mut EP_MEMORY }); 76 | 77 | let mut serial = usbd_serial::SerialPort::new(&usb_bus); 78 | 79 | let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) 80 | .strings(&[StringDescriptors::default() 81 | .manufacturer("Fake company") 82 | .product("Serial port") 83 | .serial_number("TEST") 84 | ]).unwrap() 85 | .device_class(usbd_serial::USB_CLASS_CDC) 86 | .max_packet_size_0(64) // Size required for HS, and ok for FS 87 | .unwrap() 88 | .build(); 89 | 90 | loop { 91 | if !usb_dev.poll(&mut [&mut serial]) { 92 | continue; 93 | } 94 | 95 | let mut buf = [0u8; 512]; 96 | 97 | match serial.read(&mut buf) { 98 | Ok(count) if count > 0 => { 99 | // Echo back in upper case 100 | for c in buf[0..count].iter_mut() { 101 | if 0x61 <= *c && *c <= 0x7a { 102 | *c &= !0x20; 103 | } 104 | } 105 | 106 | let mut write_offset = 0; 107 | while write_offset < count { 108 | match serial.write(&buf[write_offset..count]) { 109 | Ok(len) if len > 0 => { 110 | write_offset += len; 111 | } 112 | _ => {} 113 | } 114 | } 115 | } 116 | _ => {} 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /memory_1024_320.x: -------------------------------------------------------------------------------- 1 | /* For STM32F7{45,46,56} devices */ 2 | MEMORY 3 | { 4 | /* NOTE K = KiBi = 1024 bytes */ 5 | FLASH : ORIGIN = 0x08000000, LENGTH = 1M 6 | RAM : ORIGIN = 0x20000000, LENGTH = 64K + 240K + 16K 7 | } 8 | 9 | /* This is where the call stack will be allocated. */ 10 | /* The stack is of the full descending type. */ 11 | /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ 12 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); 13 | -------------------------------------------------------------------------------- /memory_2048_368.x: -------------------------------------------------------------------------------- 1 | /* For STM32F765,767,768,769,777,778,779 devices */ 2 | MEMORY 3 | { 4 | /* NOTE K = KiBi = 1024 bytes */ 5 | FLASH : ORIGIN = 0x08000000, LENGTH = 2M 6 | RAM : ORIGIN = 0x20020000, LENGTH = 368K + 16K 7 | ITCM : ORIGIN = 0x00000000, LENGTH = 16K /* Instruction Tighly Coupled Memory */ 8 | DTCM : ORIGIN = 0x20000000, LENGTH = 128K /* Data Tighly Coupled Memory */ 9 | } 10 | 11 | SECTIONS 12 | { 13 | .itcm : ALIGN(4) 14 | { 15 | *(.itcm .itcm.*); 16 | . = ALIGN(4); 17 | } > ITCM 18 | 19 | .dtcm : ALIGN(4) 20 | { 21 | *(.dtcm .dtcm.*); 22 | . = ALIGN(4); 23 | } > DTCM 24 | } 25 | 26 | /* You can then use something like this to place a variable into a specific section of memory: 27 | * #[link_section = ".dtcm.BUFFER"] 28 | * static mut BUF: [u8; 1024] = [3u8; 1024]; 29 | * Verifiable with: cargo size --release --example hello_world -- -A 30 | */ 31 | 32 | /* This is where the call stack will be allocated. */ 33 | /* The stack is of the full descending type. */ 34 | /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ 35 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); 36 | -------------------------------------------------------------------------------- /memory_512_176.x: -------------------------------------------------------------------------------- 1 | /* For STM32F722,723,732,733 devices */ 2 | MEMORY 3 | { 4 | /* NOTE K = KiBi = 1024 bytes */ 5 | FLASH : ORIGIN = 0x08000000, LENGTH = 512K 6 | RAM : ORIGIN = 0x20000000, LENGTH = 176K + 16K 7 | } 8 | 9 | /* This is where the call stack will be allocated. */ 10 | /* The stack is of the full descending type. */ 11 | /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ 12 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); 13 | -------------------------------------------------------------------------------- /memory_64_176.x: -------------------------------------------------------------------------------- 1 | /* For STM32F730 devices */ 2 | MEMORY 3 | { 4 | /* NOTE K = KiBi = 1024 bytes */ 5 | FLASH : ORIGIN = 0x08000000, LENGTH = 64k 6 | RAM : ORIGIN = 0x20000000, LENGTH = 176K + 16K 7 | } 8 | 9 | /* This is where the call stack will be allocated. */ 10 | /* The stack is of the full descending type. */ 11 | /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ 12 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); 13 | -------------------------------------------------------------------------------- /memory_64_240.x: -------------------------------------------------------------------------------- 1 | /* For STM32F750 devices */ 2 | MEMORY 3 | { 4 | /* NOTE K = KiBi = 1024 bytes */ 5 | FLASH : ORIGIN = 0x08000000, LENGTH = 64K 6 | RAM : ORIGIN = 0x20000000, LENGTH = 240K + 16K 7 | } 8 | 9 | /* This is where the call stack will be allocated. */ 10 | /* The stack is of the full descending type. */ 11 | /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ 12 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); 13 | -------------------------------------------------------------------------------- /openocd.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/stlink-v2-1.cfg] 2 | transport select hla_swd 3 | source [find target/stm32f7x.cfg] 4 | 5 | init 6 | arm semihosting enable 7 | reset 8 | -------------------------------------------------------------------------------- /openocd.gdb: -------------------------------------------------------------------------------- 1 | target remote :3333 2 | load 3 | continue 4 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | pre-release-replacements = [ 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 3 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 4 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 5 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 6 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/stm32-rs/{{crate_name}}/compare/{{tag_name}}...HEAD", exactly=1}, 7 | ] -------------------------------------------------------------------------------- /src/can.rs: -------------------------------------------------------------------------------- 1 | //! # Controller Area Network (CAN) Interface 2 | //! 3 | //! ## Alternate function remapping 4 | //! 5 | //! TX: Alternate Push-Pull Output 6 | //! RX: Alternate AF9 Alternate 7 | //! 8 | //! ### CAN1 9 | //! 10 | //! | Function | NoRemap | Remap | 11 | //! |----------|---------|-------| 12 | //! | TX | PA12 | PB9 | 13 | //! | RX | PA11 | PB8 | 14 | //! 15 | //! ### CAN2 16 | //! 17 | //! | Function | NoRemap | Remap | 18 | //! |----------|---------|-------| 19 | //! | TX | PB6 | PB13 | 20 | //! | RX | PB5 | PB12 | 21 | 22 | use crate::gpio::gpiob::{PB12, PB13, PB5, PB6, PB8, PB9}; 23 | use crate::gpio::{ 24 | gpioa::{PA11, PA12}, 25 | Alternate, 26 | }; 27 | use crate::pac::CAN1; 28 | use crate::pac::CAN2; 29 | use crate::rcc::APB1; 30 | 31 | mod sealed { 32 | pub trait Sealed {} 33 | } 34 | 35 | pub trait Pins: sealed::Sealed { 36 | type Instance; 37 | } 38 | 39 | impl sealed::Sealed for (PA12>, PA11>) {} 40 | impl Pins for (PA12>, PA11>) { 41 | type Instance = CAN1; 42 | } 43 | 44 | impl sealed::Sealed for (PB9>, PB8>) {} 45 | impl Pins for (PB9>, PB8>) { 46 | type Instance = CAN1; 47 | } 48 | 49 | impl sealed::Sealed for (PB6>, PB5>) {} 50 | impl Pins for (PB6>, PB5>) { 51 | type Instance = CAN2; 52 | } 53 | 54 | impl sealed::Sealed for (PB13>, PB12>) {} 55 | impl Pins for (PB13>, PB12>) { 56 | type Instance = CAN2; 57 | } 58 | 59 | /// Interface to the CAN peripheral. 60 | pub struct Can { 61 | _peripheral: Instance, 62 | } 63 | 64 | impl Can 65 | where 66 | Instance: crate::rcc::Enable, 67 | { 68 | /// Creates a CAN interaface. 69 | pub fn new

(can: Instance, apb: &mut APB1, _pins: P) -> Can 70 | where 71 | P: Pins, 72 | { 73 | Instance::enable(apb); 74 | Can { _peripheral: can } 75 | } 76 | } 77 | 78 | unsafe impl bxcan::Instance for Can { 79 | const REGISTERS: *mut bxcan::RegisterBlock = CAN1::ptr() as *mut _; 80 | } 81 | 82 | unsafe impl bxcan::Instance for Can { 83 | const REGISTERS: *mut bxcan::RegisterBlock = CAN2::ptr() as *mut _; 84 | } 85 | 86 | unsafe impl bxcan::FilterOwner for Can { 87 | const NUM_FILTER_BANKS: u8 = 28; 88 | } 89 | 90 | unsafe impl bxcan::MasterInstance for Can {} 91 | -------------------------------------------------------------------------------- /src/dac.rs: -------------------------------------------------------------------------------- 1 | //! Digital-to-analog converter 2 | 3 | use crate::pac::DAC; 4 | use crate::{ 5 | gpio::{ 6 | gpioa::{PA4, PA5}, 7 | Analog, 8 | }, 9 | rcc::{Enable, Reset}, 10 | }; 11 | 12 | /// DAC Errors 13 | #[derive(Debug)] 14 | pub enum Error { 15 | /// general 16 | error, 17 | } 18 | 19 | use core::mem; 20 | 21 | pub struct C1; 22 | pub struct C2; 23 | 24 | pub trait DacOut { 25 | fn set_value(&mut self, val: V); 26 | fn get_value(&mut self) -> V; 27 | } 28 | 29 | pub trait DacPin { 30 | fn enable(&mut self); 31 | } 32 | 33 | pub trait Pins { 34 | type Output; 35 | } 36 | 37 | impl Pins for PA4 { 38 | type Output = C1; 39 | } 40 | 41 | impl Pins for PA5 { 42 | type Output = C2; 43 | } 44 | 45 | impl Pins for (PA4, PA5) { 46 | type Output = (C1, C2); 47 | } 48 | 49 | pub fn dac(_dac: DAC, _pins: PINS) -> PINS::Output 50 | where 51 | PINS: Pins, 52 | { 53 | unsafe { 54 | DAC::enable_unchecked(); 55 | DAC::reset_unchecked(); 56 | 57 | // NOTE(unsafe) ZST, doesn't need initialization. 58 | assert!(mem::size_of::() == 0); 59 | #[allow(clippy::uninit_assumed_init)] 60 | mem::MaybeUninit::uninit().assume_init() 61 | } 62 | } 63 | 64 | macro_rules! dac { 65 | ($CX:ident, $en:ident, $cen:ident, $cal_flag:ident, $trim:ident, $mode:ident, $dhrx:ident, $dac_dor:ident, $daccxdhr:ident) => { 66 | impl DacPin for $CX { 67 | fn enable(&mut self) { 68 | let dac = unsafe { &(*DAC::ptr()) }; 69 | dac.cr.modify(|_, w| w.$en().set_bit()); 70 | } 71 | } 72 | 73 | impl DacOut for $CX { 74 | fn set_value(&mut self, val: u16) { 75 | let dac = unsafe { &(*DAC::ptr()) }; 76 | dac.$dhrx.write(|w| unsafe { w.bits(val as u32) }); 77 | } 78 | 79 | fn get_value(&mut self) -> u16 { 80 | let dac = unsafe { &(*DAC::ptr()) }; 81 | dac.$dac_dor.read().bits() as u16 82 | } 83 | } 84 | }; 85 | } 86 | 87 | pub trait DacExt { 88 | fn constrain(self, pins: PINS) -> PINS::Output 89 | where 90 | PINS: Pins; 91 | } 92 | 93 | impl DacExt for DAC { 94 | fn constrain(self, pins: PINS) -> PINS::Output 95 | where 96 | PINS: Pins, 97 | { 98 | dac(self, pins) 99 | } 100 | } 101 | 102 | dac!(C1, en1, cen1, cal_flag1, otrim1, mode1, dhr12r1, dor1, dacc1dhr); 103 | dac!(C2, en2, cen2, cal_flag2, otrim2, mode2, dhr12r2, dor2, dacc2dhr); 104 | -------------------------------------------------------------------------------- /src/flash.rs: -------------------------------------------------------------------------------- 1 | //! Flash memory 2 | 3 | use crate::pac::FLASH; 4 | use nb::block; 5 | 6 | /// Base address of flash memory on AXIM interface. 7 | const FLASH_BASE: *mut u8 = 0x800_0000 as *mut u8; 8 | 9 | /// The last valid flash address in any STM32F7 device 10 | const MAX_FLASH_ADDRESS: *mut u8 = 0x81F_FFFF as *mut u8; 11 | 12 | /// Flash programming error. 13 | #[derive(Debug, PartialEq, Eq)] 14 | pub enum Error { 15 | Busy, 16 | Locked, 17 | EraseSequence, 18 | ProgrammingParallelism, 19 | ProgrammingAlignment, 20 | WriteProtection, 21 | } 22 | 23 | /// Embedded flash memory. 24 | pub struct Flash { 25 | registers: FLASH, 26 | } 27 | 28 | impl Flash { 29 | /// Creates a new Flash instance. 30 | pub fn new(flash: FLASH) -> Self { 31 | Self { registers: flash } 32 | } 33 | 34 | /// Unlocks the flash memory. 35 | pub fn unlock(&mut self) { 36 | if !self.is_locked() { 37 | // don't try to unlock the flash if it's already unlocked, because 38 | // trying to unlock the flash twice causes a HardFault 39 | return; 40 | } 41 | 42 | self.registers.keyr.write(|w| w.key().bits(0x45670123)); 43 | self.registers.keyr.write(|w| w.key().bits(0xCDEF89AB)); 44 | } 45 | 46 | /// Locks the flash memory. 47 | pub fn lock(&mut self) { 48 | self.registers.cr.modify(|_, w| w.lock().set_bit()); 49 | } 50 | 51 | /// Returns `true` if the flash memory is locked. 52 | fn is_locked(&self) -> bool { 53 | self.registers.cr.read().lock().is_locked() 54 | } 55 | 56 | /// Returns `true` if a flash operation is in progress. 57 | fn is_busy(&self) -> bool { 58 | self.registers.sr.read().bsy().bit_is_set() 59 | } 60 | 61 | /// Starts a sector erase sequence. 62 | /// 63 | /// The returned `EraseSequence` object can be used to wait for the completion of the 64 | /// erase sequence by blocking on the `wait` method. 65 | pub fn erase_sector(&mut self, sector_number: u8) -> Result, Error> { 66 | EraseSequence::new_erase_sector(self, sector_number) 67 | } 68 | 69 | /// Erases a flash sector. 70 | /// 71 | /// This method blocks until the sector is erased or an error occurred. 72 | pub fn blocking_erase_sector(&mut self, sector_number: u8) -> Result<(), Error> { 73 | let mut sequence = self.erase_sector(sector_number)?; 74 | block!(sequence.wait()) 75 | } 76 | 77 | /// Starts a mass erases of the flash memory. 78 | /// 79 | /// The returned `EraseSequence` object can be used to wait for the completion of the 80 | /// erase sequence by blocking on the `wait` method. 81 | pub fn mass_erase(&mut self) -> Result, Error> { 82 | EraseSequence::new_mass_erase(self) 83 | } 84 | 85 | /// Mass erases the flash memory. 86 | /// 87 | /// This method blocks until the flash is erased or an error occurred. 88 | pub fn blocking_mass_erase(&mut self) -> Result<(), Error> { 89 | let mut sequence = self.mass_erase()?; 90 | block!(sequence.wait()) 91 | } 92 | 93 | /// Starts a programming sequence. 94 | /// 95 | /// Note that you must block on the `wait` method in the returned `ProgrammingSequence` object 96 | /// in order to program all bytes. 97 | pub fn program<'a, 'b>( 98 | &'a mut self, 99 | start_offset: usize, 100 | data: &'b [u8], 101 | ) -> Result, Error> { 102 | ProgrammingSequence::new(self, start_offset, data) 103 | } 104 | 105 | /// Programs a block of flash memory. 106 | /// 107 | /// This method blocks until the block is programed or an error occurred. 108 | pub fn blocking_program(&mut self, start_offset: usize, data: &[u8]) -> Result<(), Error> { 109 | let mut sequence = self.program(start_offset, data)?; 110 | block!(sequence.wait()) 111 | } 112 | 113 | /// Releases the flash peripheral. 114 | pub fn free(self) -> FLASH { 115 | self.registers 116 | } 117 | 118 | /// Returns an error if the flash is locked or busy. 119 | fn check_locked_or_busy(&self) -> Result<(), Error> { 120 | if self.is_locked() { 121 | Err(Error::Locked) 122 | } else if self.is_busy() { 123 | Err(Error::Busy) 124 | } else { 125 | Ok(()) 126 | } 127 | } 128 | 129 | /// Checks the error flags. 130 | fn check_errors(&self) -> Result<(), Error> { 131 | let sr = self.registers.sr.read(); 132 | 133 | if sr.erserr().bit_is_set() { 134 | Err(Error::EraseSequence) 135 | } else if sr.pgperr().bit_is_set() { 136 | Err(Error::ProgrammingParallelism) 137 | } else if sr.pgaerr().bit_is_set() { 138 | Err(Error::ProgrammingAlignment) 139 | } else if sr.wrperr().bit_is_set() { 140 | Err(Error::WriteProtection) 141 | } else { 142 | Ok(()) 143 | } 144 | } 145 | 146 | /// Clears all error flags. 147 | fn clear_errors(&mut self) { 148 | self.registers.sr.write(|w| { 149 | w.erserr() 150 | .set_bit() 151 | .pgperr() 152 | .set_bit() 153 | .pgaerr() 154 | .set_bit() 155 | .wrperr() 156 | .set_bit() 157 | }); 158 | } 159 | } 160 | 161 | /// Erase sequence. 162 | pub struct EraseSequence<'a> { 163 | flash: &'a mut Flash, 164 | } 165 | 166 | impl<'a> EraseSequence<'a> { 167 | /// Creates a sector erase sequence. 168 | fn new_erase_sector(flash: &'a mut Flash, sector_number: u8) -> Result { 169 | flash.check_locked_or_busy()?; 170 | flash.clear_errors(); 171 | 172 | //TODO: This should check if sector_number is valid for this device 173 | 174 | flash.registers.cr.modify(|_, w| unsafe { 175 | #[cfg(any( 176 | feature = "stm32f765", 177 | feature = "stm32f767", 178 | feature = "stm32f769", 179 | feature = "stm32f777", 180 | feature = "stm32f778", 181 | feature = "stm32f779", 182 | ))] 183 | w.mer1().clear_bit().mer2().clear_bit(); 184 | #[cfg(not(any( 185 | feature = "stm32f765", 186 | feature = "stm32f767", 187 | feature = "stm32f769", 188 | feature = "stm32f777", 189 | feature = "stm32f778", 190 | feature = "stm32f779", 191 | )))] 192 | w.mer().clear_bit(); 193 | w.ser().set_bit().snb().bits(sector_number) 194 | }); 195 | flash.registers.cr.modify(|_, w| w.strt().start()); 196 | 197 | Ok(Self { flash }) 198 | } 199 | 200 | /// Creates a mass erase sequence. 201 | fn new_mass_erase(flash: &'a mut Flash) -> Result { 202 | flash.check_locked_or_busy()?; 203 | flash.clear_errors(); 204 | 205 | flash.registers.cr.modify(|_, w| { 206 | #[cfg(any( 207 | feature = "stm32f765", 208 | feature = "stm32f767", 209 | feature = "stm32f769", 210 | feature = "stm32f777", 211 | feature = "stm32f778", 212 | feature = "stm32f779", 213 | ))] 214 | w.mer1().set_bit().mer2().set_bit(); 215 | #[cfg(not(any( 216 | feature = "stm32f765", 217 | feature = "stm32f767", 218 | feature = "stm32f769", 219 | feature = "stm32f777", 220 | feature = "stm32f778", 221 | feature = "stm32f779", 222 | )))] 223 | w.mer().set_bit(); 224 | w.ser().clear_bit() 225 | }); 226 | 227 | flash.registers.cr.modify(|_, w| w.strt().start()); 228 | 229 | Ok(Self { flash }) 230 | } 231 | 232 | /// Waits until the erase sequence is finished. 233 | pub fn wait(&mut self) -> nb::Result<(), Error> { 234 | self.flash.check_errors().map_err(nb::Error::from)?; 235 | 236 | if self.flash.is_busy() { 237 | Err(nb::Error::WouldBlock) 238 | } else { 239 | Ok(()) 240 | } 241 | } 242 | } 243 | 244 | /// Programming sequence. 245 | pub struct ProgrammingSequence<'a, 'b> { 246 | flash: &'a mut Flash, 247 | data: &'b [u8], 248 | address: *mut u8, 249 | } 250 | 251 | impl<'a, 'b> ProgrammingSequence<'a, 'b> { 252 | /// Creates a programming sequence. 253 | fn new(flash: &'a mut Flash, start_offset: usize, data: &'b [u8]) -> Result { 254 | flash.check_locked_or_busy()?; 255 | flash.clear_errors(); 256 | 257 | flash 258 | .registers 259 | .cr 260 | .modify(|_, w| w.psize().psize8().pg().set_bit()); 261 | 262 | let address = unsafe { FLASH_BASE.add(start_offset) }; 263 | 264 | Ok(Self { 265 | flash, 266 | data, 267 | address, 268 | }) 269 | } 270 | 271 | /// Waits until the programming sequence is finished. 272 | pub fn wait(&mut self) -> nb::Result<(), Error> { 273 | if self.flash.is_busy() { 274 | return Err(nb::Error::WouldBlock); 275 | } 276 | 277 | if let Err(error) = self.flash.check_errors() { 278 | // make sure programing mode is disabled when an error occurred 279 | self.flash.registers.cr.modify(|_, w| w.pg().clear_bit()); 280 | 281 | return Err(error.into()); 282 | } 283 | 284 | if let Some((first, rest)) = self.data.split_first() { 285 | if self.address >= FLASH_BASE && self.address <= MAX_FLASH_ADDRESS { 286 | unsafe { 287 | core::ptr::write_volatile(self.address, *first); 288 | } 289 | } 290 | 291 | // ensure data is written byte by byte to prevent programming parallelism errors 292 | cortex_m::asm::dmb(); 293 | 294 | self.address = unsafe { self.address.add(1) }; 295 | self.data = rest; 296 | 297 | Err(nb::Error::WouldBlock) 298 | } else { 299 | self.flash.registers.cr.modify(|_, w| w.pg().clear_bit()); 300 | 301 | Ok(()) 302 | } 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /src/fmc_lcd/display_interface_impl.rs: -------------------------------------------------------------------------------- 1 | use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; 2 | 3 | use super::{Lcd, SubBank}; 4 | 5 | impl WriteOnlyDataCommand for Lcd 6 | where 7 | S: SubBank, 8 | { 9 | fn send_commands(&mut self, cmd: DataFormat<'_>) -> Result<(), DisplayError> { 10 | match cmd { 11 | DataFormat::U8(slice) => { 12 | for value in slice { 13 | self.write_command(u16::from(*value)); 14 | } 15 | } 16 | DataFormat::U16(slice) => { 17 | for value in slice { 18 | self.write_command(*value); 19 | } 20 | } 21 | DataFormat::U16BE(slice) | DataFormat::U16LE(slice) => { 22 | // As long as the data bus is 16 bits wide, the byte order doesn't matter. 23 | for value in slice { 24 | self.write_command(*value); 25 | } 26 | } 27 | DataFormat::U8Iter(iter) => { 28 | for value in iter { 29 | self.write_command(u16::from(value)); 30 | } 31 | } 32 | DataFormat::U16BEIter(iter) | DataFormat::U16LEIter(iter) => { 33 | // As long as the data bus is 16 bits wide, the byte order doesn't matter. 34 | for value in iter { 35 | self.write_command(value); 36 | } 37 | } 38 | _ => return Err(DisplayError::DataFormatNotImplemented), 39 | } 40 | Ok(()) 41 | } 42 | 43 | fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { 44 | match buf { 45 | DataFormat::U8(slice) => { 46 | for value in slice { 47 | self.write_data(u16::from(*value)); 48 | } 49 | } 50 | DataFormat::U16(slice) => { 51 | for value in slice { 52 | self.write_data(*value); 53 | } 54 | } 55 | DataFormat::U16BE(slice) | DataFormat::U16LE(slice) => { 56 | // As long as the data bus is 16 bits wide, the byte order doesn't matter. 57 | for value in slice { 58 | self.write_data(*value); 59 | } 60 | } 61 | DataFormat::U8Iter(iter) => { 62 | for value in iter { 63 | self.write_data(u16::from(value)); 64 | } 65 | } 66 | DataFormat::U16BEIter(iter) | DataFormat::U16LEIter(iter) => { 67 | // As long as the data bus is 16 bits wide, the byte order doesn't matter. 68 | for value in iter { 69 | self.write_data(value); 70 | } 71 | } 72 | _ => return Err(DisplayError::DataFormatNotImplemented), 73 | } 74 | Ok(()) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/fmc_lcd/sealed.rs: -------------------------------------------------------------------------------- 1 | /// Private implementation details used in the fmc_lcd module and the pins submodule 2 | 3 | pub trait Sealed {} 4 | 5 | /// Private supertrait of SubBank 6 | pub trait SealedSubBank { 7 | /// The address of the beginning of this sub-bank's address space 8 | const BASE_ADDRESS: usize; 9 | /// The address in memory used to communicate with the LCD controller with the data/command 10 | /// signal set to command (low) 11 | const COMMAND_ADDRESS: usize = Self::BASE_ADDRESS; 12 | /// The address in memory used to communicate with the LCD controller with the data/command 13 | /// signal set to data (high) 14 | const DATA_ADDRESS: usize = make_data_address(Self::BASE_ADDRESS); 15 | } 16 | 17 | /// A trait similar to Default, but private to this crate 18 | /// 19 | /// This is used to create `Lcd` objects and tuples of `Lcd`s. 20 | pub trait Conjure { 21 | /// Creates something out of thin air 22 | fn conjure() -> Self; 23 | } 24 | 25 | /// Converts a command address into a data address 26 | /// 27 | /// The data address will result in all external address signals being set high. 28 | const fn make_data_address(base: usize) -> usize { 29 | // Bits 26 and 27 select the sub-bank, don't change them. 30 | // Bits 25 through 1 become address signals 24 through 0, set these high. 31 | // Bit 0 is not used with 16-bit addressing. 32 | base | 0x3fffffe 33 | } 34 | -------------------------------------------------------------------------------- /src/fmc_lcd/timing.rs: -------------------------------------------------------------------------------- 1 | //! FMC timing 2 | 3 | use super::pac::fmc; 4 | 5 | /// Memory access modes 6 | /// 7 | /// These define the general shape of a transaction and the meanings of some of the time fields. 8 | /// Refer to the microcontroller reference manual for more details. 9 | #[derive(Debug, Clone)] 10 | pub enum AccessMode { 11 | ModeA, 12 | ModeB, 13 | ModeC, 14 | ModeD, 15 | } 16 | 17 | impl AccessMode { 18 | pub(crate) fn as_read_variant(&self) -> fmc::btr::ACCMOD_A { 19 | use fmc::btr::ACCMOD_A; 20 | match *self { 21 | AccessMode::ModeA => ACCMOD_A::A, 22 | AccessMode::ModeB => ACCMOD_A::B, 23 | AccessMode::ModeC => ACCMOD_A::C, 24 | AccessMode::ModeD => ACCMOD_A::D, 25 | } 26 | } 27 | pub(crate) fn as_write_variant(&self) -> fmc::bwtr::ACCMOD_A { 28 | use fmc::bwtr::ACCMOD_A; 29 | match *self { 30 | AccessMode::ModeA => ACCMOD_A::A, 31 | AccessMode::ModeB => ACCMOD_A::B, 32 | AccessMode::ModeC => ACCMOD_A::C, 33 | AccessMode::ModeD => ACCMOD_A::D, 34 | } 35 | } 36 | } 37 | 38 | /// Timing configuration for reading or writing 39 | /// 40 | /// A `Timing` object can be created using `Timing::default()` or `Default::default()`. 41 | /// 42 | /// The default timing uses access mode C and the slowest possible timings, for maximum 43 | /// compatibility. 44 | /// 45 | /// If the LCD controller and wiring allow, you can reduce the times to make transactions faster. 46 | /// 47 | /// All time fields are in units of HCLK cycles. 48 | #[derive(Debug, Clone)] 49 | pub struct Timing { 50 | pub(crate) access_mode: AccessMode, 51 | pub(crate) bus_turnaround: u8, 52 | pub(crate) data: u8, 53 | pub(crate) address_hold: u8, 54 | pub(crate) address_setup: u8, 55 | } 56 | 57 | impl Default for Timing { 58 | /// Returns a conservative (slow) timing configuration with access mode C 59 | fn default() -> Self { 60 | Timing { 61 | access_mode: AccessMode::ModeC, 62 | bus_turnaround: Timing::BUS_TURNAROUND_MAX, 63 | data: 255, 64 | address_hold: Timing::ADDRESS_HOLD_MAX, 65 | address_setup: Timing::ADDRESS_SETUP_MAX, 66 | } 67 | } 68 | } 69 | 70 | impl Timing { 71 | /// Maximum allowed value of the bus turnaround time 72 | pub const BUS_TURNAROUND_MAX: u8 = 15; 73 | /// Minimum allowed value of the data phase time 74 | pub const DATA_MIN: u8 = 1; 75 | /// Maximum allowed value of the address hold time 76 | pub const ADDRESS_HOLD_MIN: u8 = 1; 77 | /// Maximum allowed value of the address hold time 78 | pub const ADDRESS_HOLD_MAX: u8 = 15; 79 | /// Maximum allowed value of the address setup time 80 | pub const ADDRESS_SETUP_MAX: u8 = 15; 81 | 82 | /// Sets the access mode 83 | pub fn access_mode(self, access_mode: AccessMode) -> Self { 84 | Timing { 85 | access_mode, 86 | ..self 87 | } 88 | } 89 | /// Sets the bus turnaround time, in units of HCLK cycles 90 | /// 91 | /// This corresponds to the BUSTURN field of FMC_BTR or FMC_BWTR. 92 | pub fn bus_turnaround(self, bus_turnaround: u8) -> Self { 93 | Timing { 94 | bus_turnaround, 95 | ..self 96 | } 97 | } 98 | /// Sets the data phase time, in units of HCLK cycles 99 | /// 100 | /// This corresponds to the DATAST field of FMC_BTR or FMC_BWTR. 101 | pub fn data(self, data: u8) -> Self { 102 | Timing { data, ..self } 103 | } 104 | /// Sets the address hold phase time, in units of HCLK cycles 105 | /// 106 | /// This corresponds to the ADDHLD field of FMC_BTR or FMC_BWTR. 107 | pub fn address_hold(self, address_hold: u8) -> Self { 108 | Timing { 109 | address_hold, 110 | ..self 111 | } 112 | } 113 | /// Sets the address setup phase time, in units of HCLK cycles 114 | /// 115 | /// This corresponds to the ADDSET field of FMC_BTR or FMC_BWTR. 116 | pub fn address_setup(self, address_setup: u8) -> Self { 117 | Timing { 118 | address_setup, 119 | ..self 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/gpio/dynamic.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Pin type with dynamic mode 4 | /// 5 | /// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc. 6 | /// - `N` is pin number: from `0` to `15`. 7 | pub struct DynamicPin { 8 | /// Current pin mode 9 | pub(crate) mode: Dynamic, 10 | } 11 | 12 | /// Tracks the current pin state for dynamic pins 13 | pub enum Dynamic { 14 | InputFloating, 15 | InputPullUp, 16 | InputPullDown, 17 | OutputPushPull, 18 | OutputOpenDrain, 19 | } 20 | 21 | #[derive(Debug, PartialEq)] 22 | pub enum PinModeError { 23 | IncorrectMode, 24 | } 25 | 26 | impl Dynamic { 27 | pub fn is_input(&self) -> bool { 28 | use Dynamic::*; 29 | match self { 30 | InputFloating | InputPullUp | InputPullDown | OutputOpenDrain => true, 31 | OutputPushPull => false, 32 | } 33 | } 34 | pub fn is_output(&self) -> bool { 35 | use Dynamic::*; 36 | match self { 37 | InputFloating | InputPullUp | InputPullDown => false, 38 | OutputPushPull | OutputOpenDrain => true, 39 | } 40 | } 41 | } 42 | 43 | // For convertion simplify 44 | struct Unknown; 45 | 46 | impl DynamicPin { 47 | pub const fn new(mode: Dynamic) -> Self { 48 | Self { mode } 49 | } 50 | 51 | #[inline] 52 | pub fn make_pull_up_input(&mut self) { 53 | // NOTE(unsafe), we have a mutable reference to the current pin 54 | Pin::::new().into_pull_up_input(); 55 | self.mode = Dynamic::InputPullUp; 56 | } 57 | #[inline] 58 | pub fn make_pull_down_input(&mut self) { 59 | // NOTE(unsafe), we have a mutable reference to the current pin 60 | Pin::::new().into_pull_down_input(); 61 | self.mode = Dynamic::InputPullDown; 62 | } 63 | #[inline] 64 | pub fn make_floating_input(&mut self) { 65 | // NOTE(unsafe), we have a mutable reference to the current pin 66 | Pin::::new().into_floating_input(); 67 | self.mode = Dynamic::InputFloating; 68 | } 69 | #[inline] 70 | pub fn make_push_pull_output(&mut self) { 71 | // NOTE(unsafe), we have a mutable reference to the current pin 72 | Pin::::new().into_push_pull_output(); 73 | self.mode = Dynamic::OutputPushPull; 74 | } 75 | #[inline] 76 | pub fn make_push_pull_output_in_state(&mut self, state: PinState) { 77 | // NOTE(unsafe), we have a mutable reference to the current pin 78 | Pin::::new().into_push_pull_output_in_state(state); 79 | self.mode = Dynamic::OutputPushPull; 80 | } 81 | #[inline] 82 | pub fn make_open_drain_output(&mut self) { 83 | // NOTE(unsafe), we have a mutable reference to the current pin 84 | Pin::::new().into_open_drain_output(); 85 | self.mode = Dynamic::OutputOpenDrain; 86 | } 87 | #[inline] 88 | pub fn make_open_drain_output_in_state(&mut self, state: PinState) { 89 | // NOTE(unsafe), we have a mutable reference to the current pin 90 | Pin::::new().into_open_drain_output_in_state(state); 91 | self.mode = Dynamic::OutputOpenDrain; 92 | } 93 | 94 | pub fn set_high(&mut self) -> Result<(), PinModeError> { 95 | if self.mode.is_output() { 96 | Pin::::new()._set_state(PinState::High); 97 | Ok(()) 98 | } else { 99 | Err(PinModeError::IncorrectMode) 100 | } 101 | } 102 | pub fn set_low(&mut self) -> Result<(), PinModeError> { 103 | if self.mode.is_output() { 104 | Pin::::new()._set_state(PinState::Low); 105 | Ok(()) 106 | } else { 107 | Err(PinModeError::IncorrectMode) 108 | } 109 | } 110 | 111 | pub fn is_high(&self) -> Result { 112 | self.is_low().map(|b| !b) 113 | } 114 | pub fn is_low(&self) -> Result { 115 | if self.mode.is_input() { 116 | Ok(Pin::::new()._is_low()) 117 | } else { 118 | Err(PinModeError::IncorrectMode) 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/gpio/erased.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub type EPin = ErasedPin; 4 | 5 | /// Fully erased pin 6 | /// 7 | /// `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). 8 | pub struct ErasedPin { 9 | // Bits 0-3: Pin, Bits 4-7: Port 10 | pin_port: u8, 11 | _mode: PhantomData, 12 | } 13 | 14 | impl fmt::Debug for ErasedPin { 15 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 16 | formatter.write_fmt(format_args!( 17 | "P({}{})<{}>", 18 | self.port_id(), 19 | self.pin_id(), 20 | crate::stripped_type_name::() 21 | )) 22 | } 23 | } 24 | 25 | impl PinExt for ErasedPin { 26 | type Mode = MODE; 27 | 28 | #[inline(always)] 29 | fn pin_id(&self) -> u8 { 30 | self.pin_port & 0x0f 31 | } 32 | #[inline(always)] 33 | fn port_id(&self) -> u8 { 34 | self.pin_port >> 4 35 | } 36 | } 37 | 38 | impl ErasedPin { 39 | pub(crate) fn new(port: u8, pin: u8) -> Self { 40 | Self { 41 | pin_port: port << 4 | pin, 42 | _mode: PhantomData, 43 | } 44 | } 45 | 46 | #[inline] 47 | fn block(&self) -> &crate::pac::gpioa::RegisterBlock { 48 | // This function uses pointer arithmetic instead of branching to be more efficient 49 | 50 | // The logic relies on the following assumptions: 51 | // - GPIOA register is available on all chips 52 | // - all gpio register blocks have the same layout 53 | // - consecutive gpio register blocks have the same offset between them, namely 0x0400 54 | // - ErasedPin::new was called with a valid port 55 | 56 | // FIXME could be calculated after const_raw_ptr_to_usize_cast stabilization #51910 57 | const GPIO_REGISTER_OFFSET: usize = 0x0400; 58 | 59 | let offset = GPIO_REGISTER_OFFSET * self.port_id() as usize; 60 | let block_ptr = 61 | (crate::pac::GPIOA::ptr() as usize + offset) as *const crate::pac::gpioa::RegisterBlock; 62 | 63 | unsafe { &*block_ptr } 64 | } 65 | } 66 | 67 | impl ErasedPin> { 68 | #[inline(always)] 69 | pub fn set_high(&mut self) { 70 | // NOTE(unsafe) atomic write to a stateless register 71 | unsafe { self.block().bsrr.write(|w| w.bits(1 << self.pin_id())) }; 72 | } 73 | 74 | #[inline(always)] 75 | pub fn set_low(&mut self) { 76 | // NOTE(unsafe) atomic write to a stateless register 77 | unsafe { 78 | self.block() 79 | .bsrr 80 | .write(|w| w.bits(1 << (self.pin_id() + 16))) 81 | }; 82 | } 83 | 84 | #[inline(always)] 85 | pub fn get_state(&self) -> PinState { 86 | if self.is_set_low() { 87 | PinState::Low 88 | } else { 89 | PinState::High 90 | } 91 | } 92 | 93 | #[inline(always)] 94 | pub fn set_state(&mut self, state: PinState) { 95 | match state { 96 | PinState::Low => self.set_low(), 97 | PinState::High => self.set_high(), 98 | } 99 | } 100 | 101 | #[inline(always)] 102 | pub fn is_set_high(&self) -> bool { 103 | !self.is_set_low() 104 | } 105 | 106 | #[inline(always)] 107 | pub fn is_set_low(&self) -> bool { 108 | self.block().odr.read().bits() & (1 << self.pin_id()) == 0 109 | } 110 | 111 | #[inline(always)] 112 | pub fn toggle(&mut self) { 113 | if self.is_set_low() { 114 | self.set_high() 115 | } else { 116 | self.set_low() 117 | } 118 | } 119 | } 120 | 121 | impl ErasedPin> { 122 | #[inline(always)] 123 | pub fn is_high(&self) -> bool { 124 | !self.is_low() 125 | } 126 | 127 | #[inline(always)] 128 | pub fn is_low(&self) -> bool { 129 | self.block().idr.read().bits() & (1 << self.pin_id()) == 0 130 | } 131 | } 132 | 133 | impl ErasedPin> { 134 | #[inline(always)] 135 | pub fn is_high(&self) -> bool { 136 | !self.is_low() 137 | } 138 | 139 | #[inline(always)] 140 | pub fn is_low(&self) -> bool { 141 | self.block().idr.read().bits() & (1 << self.pin_id()) == 0 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/gpio/partially_erased.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub type PEPin = PartiallyErasedPin; 4 | 5 | /// Partially erased pin 6 | /// 7 | /// - `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). 8 | /// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc. 9 | pub struct PartiallyErasedPin { 10 | i: u8, 11 | _mode: PhantomData, 12 | } 13 | 14 | impl PartiallyErasedPin { 15 | pub(crate) fn new(i: u8) -> Self { 16 | Self { 17 | i, 18 | _mode: PhantomData, 19 | } 20 | } 21 | } 22 | 23 | impl fmt::Debug for PartiallyErasedPin { 24 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 25 | formatter.write_fmt(format_args!( 26 | "P{}({})<{}>", 27 | P, 28 | self.i, 29 | crate::stripped_type_name::() 30 | )) 31 | } 32 | } 33 | 34 | impl PinExt for PartiallyErasedPin { 35 | type Mode = MODE; 36 | 37 | #[inline(always)] 38 | fn pin_id(&self) -> u8 { 39 | self.i 40 | } 41 | #[inline(always)] 42 | fn port_id(&self) -> u8 { 43 | P as u8 - b'A' 44 | } 45 | } 46 | 47 | impl PartiallyErasedPin> { 48 | #[inline(always)] 49 | pub fn set_high(&mut self) { 50 | // NOTE(unsafe) atomic write to a stateless register 51 | unsafe { (*Gpio::

::ptr()).bsrr.write(|w| w.bits(1 << self.i)) } 52 | } 53 | 54 | #[inline(always)] 55 | pub fn set_low(&mut self) { 56 | // NOTE(unsafe) atomic write to a stateless register 57 | unsafe { 58 | (*Gpio::

::ptr()) 59 | .bsrr 60 | .write(|w| w.bits(1 << (self.i + 16))) 61 | } 62 | } 63 | 64 | #[inline(always)] 65 | pub fn get_state(&self) -> PinState { 66 | if self.is_set_low() { 67 | PinState::Low 68 | } else { 69 | PinState::High 70 | } 71 | } 72 | 73 | #[inline(always)] 74 | pub fn set_state(&mut self, state: PinState) { 75 | match state { 76 | PinState::Low => self.set_low(), 77 | PinState::High => self.set_high(), 78 | } 79 | } 80 | 81 | #[inline(always)] 82 | pub fn is_set_high(&self) -> bool { 83 | !self.is_set_low() 84 | } 85 | 86 | #[inline(always)] 87 | pub fn is_set_low(&self) -> bool { 88 | // NOTE(unsafe) atomic read with no side effects 89 | unsafe { (*Gpio::

::ptr()).odr.read().bits() & (1 << self.i) == 0 } 90 | } 91 | 92 | #[inline(always)] 93 | pub fn toggle(&mut self) { 94 | if self.is_set_low() { 95 | self.set_high() 96 | } else { 97 | self.set_low() 98 | } 99 | } 100 | } 101 | 102 | impl PartiallyErasedPin> { 103 | #[inline(always)] 104 | pub fn is_high(&self) -> bool { 105 | !self.is_low() 106 | } 107 | 108 | #[inline(always)] 109 | pub fn is_low(&self) -> bool { 110 | // NOTE(unsafe) atomic read with no side effects 111 | unsafe { (*Gpio::

::ptr()).idr.read().bits() & (1 << self.i) == 0 } 112 | } 113 | } 114 | 115 | impl PartiallyErasedPin> { 116 | #[inline(always)] 117 | pub fn is_high(&self) -> bool { 118 | !self.is_low() 119 | } 120 | 121 | #[inline(always)] 122 | pub fn is_low(&self) -> bool { 123 | // NOTE(unsafe) atomic read with no side effects 124 | unsafe { (*Gpio::

::ptr()).idr.read().bits() & (1 << self.i) == 0 } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! HAL for the STM32F7xx family of microcontrollers 2 | 3 | #![cfg_attr(not(test), no_std)] 4 | #![allow(non_camel_case_types)] 5 | 6 | #[cfg(not(feature = "device-selected"))] 7 | compile_error!( 8 | "This crate requires one of the following device features enabled: 9 | stm32f722 10 | stm32f723 11 | stm32f730 12 | stm32f730-lpc 13 | stm32f732 14 | stm32f733 15 | stm32f745 16 | stm32f746 17 | stm32f756 18 | stm32f765 19 | stm32f767 20 | stm32f769 21 | stm32f777 22 | stm32f778 23 | stm32f779 24 | " 25 | ); 26 | 27 | pub(crate) use embedded_hal as hal; 28 | 29 | #[cfg(feature = "stm32f722")] 30 | pub use stm32f7::stm32f7x2 as pac; 31 | 32 | #[cfg(feature = "stm32f723")] 33 | pub use stm32f7::stm32f7x3 as pac; 34 | 35 | #[cfg(any(feature = "stm32f730", feature = "stm32f730-lpc"))] 36 | pub use stm32f7::stm32f730 as pac; 37 | 38 | #[cfg(feature = "stm32f732")] 39 | pub use stm32f7::stm32f7x2 as pac; 40 | 41 | #[cfg(feature = "stm32f733")] 42 | pub use stm32f7::stm32f7x3 as pac; 43 | 44 | #[cfg(feature = "stm32f745")] 45 | pub use stm32f7::stm32f745 as pac; 46 | 47 | #[cfg(feature = "stm32f746")] 48 | pub use stm32f7::stm32f7x6 as pac; 49 | 50 | #[cfg(feature = "stm32f756")] 51 | pub use stm32f7::stm32f7x6 as pac; 52 | 53 | #[cfg(feature = "stm32f765")] 54 | pub use stm32f7::stm32f765 as pac; 55 | 56 | #[cfg(feature = "stm32f767")] 57 | pub use stm32f7::stm32f7x7 as pac; 58 | 59 | #[cfg(feature = "stm32f769")] 60 | pub use stm32f7::stm32f7x9 as pac; 61 | 62 | #[cfg(feature = "stm32f777")] 63 | pub use stm32f7::stm32f7x7 as pac; 64 | 65 | #[cfg(feature = "stm32f778")] 66 | pub use stm32f7::stm32f7x9 as pac; 67 | 68 | #[cfg(feature = "stm32f779")] 69 | pub use stm32f7::stm32f7x9 as pac; 70 | 71 | // Enable use of interrupt macro 72 | #[cfg(feature = "rt")] 73 | pub use crate::pac::interrupt; 74 | 75 | #[cfg(all(feature = "device-selected", feature = "has-can"))] 76 | pub mod can; 77 | 78 | #[cfg(feature = "device-selected")] 79 | pub mod dma; 80 | 81 | #[cfg(all(feature = "device-selected", feature = "fmc"))] 82 | pub mod fmc; 83 | 84 | #[cfg(all(feature = "fmc_lcd", feature = "device-selected", feature = "fmc"))] 85 | pub mod fmc_lcd; 86 | 87 | #[cfg(feature = "device-selected")] 88 | pub mod gpio; 89 | 90 | #[cfg(feature = "device-selected")] 91 | pub mod dac; 92 | 93 | #[cfg(all( 94 | feature = "usb_fs", 95 | any( 96 | feature = "stm32f722", 97 | feature = "stm32f723", 98 | feature = "stm32f730", 99 | feature = "stm32f730-lpc", 100 | feature = "stm32f732", 101 | feature = "stm32f733", 102 | feature = "stm32f746", 103 | feature = "stm32f767", 104 | ) 105 | ))] 106 | pub mod otg_fs; 107 | 108 | #[cfg(all( 109 | feature = "usb_hs", 110 | any( 111 | feature = "stm32f722", 112 | feature = "stm32f723", 113 | feature = "stm32f730", 114 | feature = "stm32f730-lpc", 115 | feature = "stm32f732", 116 | feature = "stm32f733", 117 | feature = "stm32f746", 118 | feature = "stm32f767", 119 | ) 120 | ))] 121 | pub mod otg_hs; 122 | 123 | #[cfg(feature = "device-selected")] 124 | pub mod prelude; 125 | 126 | #[cfg(feature = "device-selected")] 127 | pub mod rcc; 128 | 129 | #[cfg(feature = "device-selected")] 130 | pub mod rtc; 131 | 132 | #[cfg(feature = "device-selected")] 133 | pub mod serial; 134 | 135 | #[cfg(feature = "device-selected")] 136 | pub mod spi; 137 | 138 | #[cfg(feature = "device-selected")] 139 | pub mod timer; 140 | 141 | #[cfg(feature = "device-selected")] 142 | pub mod signature; 143 | 144 | #[cfg(feature = "device-selected")] 145 | pub mod i2c; 146 | 147 | #[cfg(feature = "device-selected")] 148 | pub mod rng; 149 | 150 | #[cfg(feature = "device-selected")] 151 | pub mod qspi; 152 | 153 | #[cfg(any(feature = "stm32f765", feature = "stm32f767", feature = "stm32f769"))] 154 | pub mod adc; 155 | 156 | #[cfg(any(feature = "stm32f767", feature = "stm32f769"))] 157 | pub mod qei; 158 | 159 | #[cfg(feature = "ltdc")] 160 | pub mod ltdc; 161 | 162 | #[cfg(feature = "device-selected")] 163 | pub mod flash; 164 | 165 | #[cfg(feature = "device-selected")] 166 | pub mod watchdog; 167 | 168 | pub mod state { 169 | /// Indicates that a peripheral is enabled 170 | pub struct Enabled; 171 | 172 | /// Indicates that a peripheral is disabled 173 | pub struct Disabled; 174 | } 175 | 176 | #[cfg(feature = "device-selected")] 177 | mod sealed { 178 | pub trait Sealed {} 179 | } 180 | #[cfg(feature = "device-selected")] 181 | pub(crate) use sealed::Sealed; 182 | 183 | fn stripped_type_name() -> &'static str { 184 | let s = core::any::type_name::(); 185 | let p = s.split("::"); 186 | p.last().unwrap() 187 | } 188 | 189 | /// Bits per second 190 | pub type BitsPerSecond = fugit::HertzU32; 191 | 192 | /// Extension trait that adds convenience methods to the `u32` type 193 | pub trait U32Ext { 194 | /// Wrap in `Bps` 195 | fn bps(self) -> BitsPerSecond; 196 | } 197 | 198 | impl U32Ext for u32 { 199 | fn bps(self) -> BitsPerSecond { 200 | BitsPerSecond::from_raw(self) 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/otg_fs.rs: -------------------------------------------------------------------------------- 1 | //! USB OTG full-speed peripheral 2 | //! 3 | //! Requires the `usb_fs` feature. 4 | //! Only one of the `usb_fs`/`usb_hs` features can be selected at the same time. 5 | 6 | use crate::pac; 7 | 8 | use crate::gpio::{ 9 | gpioa::{PA11, PA12}, 10 | Alternate, 11 | }; 12 | use crate::rcc::{BusClock, Clocks, Enable, Reset}; 13 | use fugit::HertzU32 as Hertz; 14 | 15 | pub use synopsys_usb_otg::UsbBus; 16 | use synopsys_usb_otg::UsbPeripheral; 17 | 18 | pub struct USB { 19 | pub usb_global: pac::OTG_FS_GLOBAL, 20 | pub usb_device: pac::OTG_FS_DEVICE, 21 | pub usb_pwrclk: pac::OTG_FS_PWRCLK, 22 | pub pin_dm: PA11>, 23 | pub pin_dp: PA12>, 24 | pub hclk: Hertz, 25 | } 26 | 27 | impl USB { 28 | /// Construct a USB peripheral wrapper. 29 | /// 30 | /// Call `UsbBus::new` to construct and initialize the USB peripheral driver. 31 | pub fn new( 32 | usb_global: pac::OTG_FS_GLOBAL, 33 | usb_device: pac::OTG_FS_DEVICE, 34 | usb_pwrclk: pac::OTG_FS_PWRCLK, 35 | pins: (PA11>, PA12>), 36 | clocks: &Clocks, 37 | ) -> Self { 38 | Self { 39 | usb_global, 40 | usb_device, 41 | usb_pwrclk, 42 | pin_dm: pins.0, 43 | pin_dp: pins.1, 44 | hclk: pac::OTG_FS_GLOBAL::clock(clocks), 45 | } 46 | } 47 | } 48 | 49 | unsafe impl Sync for USB {} 50 | 51 | unsafe impl UsbPeripheral for USB { 52 | const REGISTERS: *const () = pac::OTG_FS_GLOBAL::ptr() as *const (); 53 | 54 | const HIGH_SPEED: bool = false; 55 | const FIFO_DEPTH_WORDS: usize = 320; 56 | const ENDPOINT_COUNT: usize = 6; 57 | 58 | fn enable() { 59 | cortex_m::interrupt::free(|_| unsafe { 60 | // Enable USB peripheral 61 | pac::OTG_FS_GLOBAL::enable_unchecked(); 62 | 63 | // Reset USB peripheral 64 | pac::OTG_FS_GLOBAL::reset_unchecked(); 65 | }); 66 | } 67 | 68 | fn ahb_frequency_hz(&self) -> u32 { 69 | self.hclk.raw() 70 | } 71 | } 72 | 73 | pub type UsbBusType = UsbBus; 74 | -------------------------------------------------------------------------------- /src/otg_hs.rs: -------------------------------------------------------------------------------- 1 | //! USB OTG high-speed peripheral 2 | //! 3 | //! Requires the `usb_hs` feature. 4 | //! Only one of the `usb_fs`/`usb_hs` features can be selected at the same time. 5 | 6 | use crate::pac; 7 | 8 | use crate::gpio::{ 9 | gpiob::{PB14, PB15}, 10 | Alternate, 11 | }; 12 | use crate::rcc::{BusClock, Clocks, Enable, Reset}; 13 | use fugit::{HertzU32 as Hertz, RateExtU32}; 14 | 15 | #[cfg(feature = "usb_hs_phy")] 16 | use synopsys_usb_otg::PhyType; 17 | pub use synopsys_usb_otg::UsbBus; 18 | use synopsys_usb_otg::UsbPeripheral; 19 | 20 | pub struct USB { 21 | pub usb_global: pac::OTG_HS_GLOBAL, 22 | pub usb_device: pac::OTG_HS_DEVICE, 23 | pub usb_pwrclk: pac::OTG_HS_PWRCLK, 24 | #[cfg(feature = "usb_hs_phy")] 25 | pub usb_phy: Option, 26 | pub pin_dm: PB14>, 27 | pub pin_dp: PB15>, 28 | pub hclk: Hertz, 29 | #[cfg(feature = "usb_hs_phy")] 30 | pub hse: Hertz, 31 | } 32 | 33 | impl USB { 34 | /// Construct a USB peripheral wrapper. 35 | /// 36 | /// Call `UsbBus::new` to construct and initialize the USB peripheral driver. 37 | pub fn new( 38 | usb_global: pac::OTG_HS_GLOBAL, 39 | usb_device: pac::OTG_HS_DEVICE, 40 | usb_pwrclk: pac::OTG_HS_PWRCLK, 41 | pins: (PB14>, PB15>), 42 | clocks: Clocks, 43 | ) -> Self { 44 | Self { 45 | usb_global, 46 | usb_device, 47 | usb_pwrclk, 48 | #[cfg(feature = "usb_hs_phy")] 49 | usb_phy: None, 50 | pin_dm: pins.0, 51 | pin_dp: pins.1, 52 | hclk: clocks.hclk(), 53 | #[cfg(feature = "usb_hs_phy")] 54 | hse: clocks.hse().unwrap_or_else(|| 0.Hz()), 55 | } 56 | } 57 | 58 | #[cfg(feature = "usb_hs_phy")] 59 | /// Construct a USB peripheral wrapper with internal HighSpeed PHY. 60 | /// 61 | /// Call `UsbBus::new` to construct and initialize the USB peripheral driver. 62 | pub fn new_with_internal_hs_phy( 63 | usb_global: pac::OTG_HS_GLOBAL, 64 | usb_device: pac::OTG_HS_DEVICE, 65 | usb_pwrclk: pac::OTG_HS_PWRCLK, 66 | usb_phy: pac::USBPHYC, 67 | pins: (PB14>, PB15>), 68 | clocks: &Clocks, 69 | ) -> Self { 70 | Self { 71 | usb_global, 72 | usb_device, 73 | usb_pwrclk, 74 | usb_phy: Some(usb_phy), 75 | pin_dm: pins.0, 76 | pin_dp: pins.1, 77 | hclk: pac::OTG_HS_GLOBAL::clock(clocks), 78 | hse: clocks.hse().expect("HSE should be enabled"), 79 | } 80 | } 81 | } 82 | 83 | unsafe impl Sync for USB {} 84 | 85 | unsafe impl UsbPeripheral for USB { 86 | const REGISTERS: *const () = pac::OTG_HS_GLOBAL::ptr() as *const (); 87 | 88 | const HIGH_SPEED: bool = true; 89 | const FIFO_DEPTH_WORDS: usize = 1024; 90 | const ENDPOINT_COUNT: usize = 9; 91 | 92 | fn enable() { 93 | cortex_m::interrupt::free(|_| unsafe { 94 | // Enable USB peripheral 95 | pac::OTG_HS_GLOBAL::enable_unchecked(); 96 | 97 | // Reset USB peripheral 98 | pac::OTG_HS_GLOBAL::reset_unchecked(); 99 | 100 | #[cfg(feature = "usb_hs_phy")] 101 | { 102 | // Enable and reset HS PHY 103 | let rcc = &*pac::RCC::ptr(); 104 | rcc.ahb1enr.modify(|_, w| w.otghsulpien().enabled()); 105 | pac::USBPHYC::enable_unchecked(); 106 | pac::USBPHYC::reset_unchecked(); 107 | } 108 | }); 109 | } 110 | 111 | fn ahb_frequency_hz(&self) -> u32 { 112 | self.hclk.raw() 113 | } 114 | 115 | #[cfg(feature = "usb_hs_phy")] 116 | #[inline(always)] 117 | fn phy_type(&self) -> PhyType { 118 | if self.usb_phy.is_some() { 119 | PhyType::InternalHighSpeed 120 | } else { 121 | PhyType::InternalFullSpeed 122 | } 123 | } 124 | 125 | #[cfg(feature = "usb_hs_phy")] 126 | // Setup LDO and PLL 127 | fn setup_internal_hs_phy(&self) { 128 | let phy = if let Some(phy) = self.usb_phy.as_ref() { 129 | phy 130 | } else { 131 | // This should never happen as this function is only called when 132 | // phy_type() is PhyType::InternalHighSpeed and it's possible only 133 | // when self.usb_phy is not None 134 | unreachable!() 135 | }; 136 | 137 | // Calculate PLL1SEL 138 | let pll1sel = match self.hse.raw() { 139 | 12_000_000 => 0b000, 140 | 12_500_000 => 0b001, 141 | 16_000_000 => 0b011, 142 | 24_000_000 => 0b100, 143 | 25_000_000 => 0b101, 144 | _ => panic!("HSE frequency is invalid for USBPHYC"), 145 | }; 146 | 147 | // Turn on LDO 148 | // For some reason setting the bit enables the LDO 149 | phy.ldo.modify(|_, w| w.ldo_disable().set_bit()); 150 | 151 | // Busy wait until ldo_status becomes true 152 | // Notice, this may hang 153 | while phy.ldo.read().ldo_status().bit_is_clear() {} 154 | 155 | // Setup PLL 156 | // This disables the the pll1 during tuning 157 | phy.pll1.write(|w| unsafe { w.pll1sel().bits(pll1sel) }); 158 | 159 | phy.tune.modify(|r, w| unsafe { w.bits(r.bits() | 0xF13) }); 160 | 161 | phy.pll1.modify(|_, w| w.pll1en().set_bit()); 162 | 163 | // 2ms Delay required to get internal phy clock stable 164 | cortex_m::asm::delay(432000); 165 | } 166 | } 167 | 168 | pub type UsbBusType = UsbBus; 169 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use fugit::{ExtU32 as _, RateExtU32 as _}; 2 | 3 | #[cfg(feature = "fmc")] 4 | pub use crate::fmc::FmcExt as _stm327xx_hal_fmc_FmcExt; 5 | 6 | pub use crate::gpio::GpioExt as _stm327xx_hal_gpio_GpioExt; 7 | pub use crate::hal::digital::v2::{InputPin, OutputPin}; 8 | pub use crate::hal::prelude::*; 9 | pub use crate::rcc::RccExt as _stm32f7xx_hal_rcc_RccExt; 10 | pub use crate::rng::RngExt as _; 11 | #[cfg(feature = "rtic")] 12 | pub use crate::timer::MonoTimerExt as _; 13 | pub use crate::timer::PwmExt as _; 14 | pub use crate::timer::SysTimerExt as _; 15 | pub use crate::timer::TimerExt as _; 16 | pub use crate::U32Ext as _; 17 | -------------------------------------------------------------------------------- /src/qei.rs: -------------------------------------------------------------------------------- 1 | //! Quadrature Encoder Interface API 2 | 3 | use crate::rcc::{Enable, Reset, APB1}; 4 | #[cfg(feature = "stm32f767")] 5 | use stm32f7::stm32f7x7::{TIM2, TIM3, TIM4, TIM5}; 6 | 7 | #[cfg(feature = "stm32f769")] 8 | use stm32f7::stm32f7x9::{TIM2, TIM3, TIM4, TIM5}; 9 | 10 | #[derive(Debug)] 11 | pub enum Direction { 12 | Upcounting, 13 | Downcounting, 14 | } 15 | 16 | /// SMS[3:0] (Slave Mode Selection) register 17 | #[derive(Debug, Clone, Copy)] 18 | pub enum SlaveMode { 19 | /// Slave mode disabled - if CEN = ‘1’ then the prescaler is clocked directly by the internal 20 | /// clock. 21 | Disable = 0b0000, 22 | 23 | /// Counter counts up/down on TI2FP1 edge depending on TI1FP2 level. 24 | EncoderMode1 = 0b0001, 25 | 26 | /// Encoder mode 2 - Counter counts up/down on TI1FP2 edge depending on TI2FP1 level. 27 | EncoderMode2 = 0b0010, 28 | 29 | /// Encoder mode 3 - Counter counts up/down on both TI1FP1 and TI2FP2 edges depending on the 30 | /// level of the other input. 31 | EncoderMode3 = 0b0011, 32 | 33 | /// Reset Mode - Rising edge of the selected trigger input (TRGI) reinitializes the counter and 34 | /// generates an update of the registers. 35 | ResetMode = 0b0100, 36 | 37 | /// Gated Mode - The counter clock is enabled when the trigger input (TRGI) is high. The 38 | /// counter stops (but is not reset) as soon as the trigger becomes low. Both start and stop of 39 | /// the counter are controlled. 40 | GatedMode = 0b0101, 41 | 42 | /// Trigger Mode - The counter starts at a rising edge of the trigger TRGI (but it is not 43 | /// reset). Only the start of the counter is controlled. 44 | TriggerMode = 0b0110, 45 | 46 | /// External Clock Mode 1 - Rising edges of the selected trigger (TRGI) clock the counter. 47 | ExternalClockMode1 = 0b0111, 48 | 49 | /// Combined reset + trigger mode - Rising edge of the selected trigger input (TRGI) 50 | /// reinitializes the counter, generates an update of the registers and starts the counter. 51 | Combined = 0b1000, 52 | } 53 | 54 | /// Quadrature Encoder Interface (QEI) options 55 | #[derive(Debug, Clone, Copy)] 56 | pub struct QeiOptions { 57 | /// Encoder slave mode 58 | pub slave_mode: SlaveMode, 59 | 60 | /// Autoreload value 61 | /// 62 | /// This value allows the maximum count to be configured. Setting a lower value 63 | /// will overflow the counter to 0 sooner. 64 | pub auto_reload_value: u32, 65 | } 66 | 67 | impl Default for QeiOptions { 68 | fn default() -> Self { 69 | Self { 70 | slave_mode: SlaveMode::EncoderMode3, 71 | auto_reload_value: core::u32::MAX, 72 | } 73 | } 74 | } 75 | 76 | /// 77 | /// Make sure that pin_ch1 and pin_ch2 are used in the corresponding alternative mode. 78 | /// ---------------------------------- 79 | /// TIMx | PIN_CH1 | PIN_CH2 | 80 | /// -------|----------|----------| 81 | /// TIM2 | PA0 \ 1 | PB3 \ 1 | 82 | /// TIM2 | PA0 \ 1 | PA1 \ 1 | 83 | /// TIM2 | PA5 \ 1 | PB3 \ 1 | 84 | /// TIM2 | PA5 \ 1 | PA1 \ 1 | 85 | /// TIM2 | PA15 \ 1 | PB3 \ 1 | 86 | /// TIM2 | PA15 \ 1 | PA1 \ 1 | 87 | /// TIM3 | PA6 \ 2 | PA7 \ 2 | 88 | /// TIM3 | PA6 \ 2 | PB5 \ 2 | 89 | /// TIM3 | PA6 \ 2 | PC7 \ 2 | 90 | /// TIM3 | PB4 \ 2 | PA7 \ 2 | 91 | /// TIM3 | PB4 \ 2 | PB5 \ 2 | 92 | /// TIM3 | PB4 \ 2 | PC7 \ 2 | 93 | /// TIM3 | PC6 \ 2 | PA7 \ 2 | 94 | /// TIM3 | PC6 \ 2 | PB5 \ 2 | 95 | /// TIM3 | PC6 \ 2 | PC7 \ 2 | 96 | /// TIM4 | PB6 \ 2 | PB7 \ 2 | 97 | /// TIM4 | PB6 \ 2 | PD13 \ 2 | 98 | /// TIM4 | PD12 \ 2 | PB7 \ 2 | 99 | /// TIM4 | PD12 \ 2 | PD13 \ 2 | 100 | /// TIM4 | PD12 \ 2 | PD13 \ 2 | 101 | /// TIM5 | PA0 \ 2 | PA1 \ 2 | 102 | pub struct Qei { 103 | tim: TIM, 104 | _pin_ch1: PIN1, 105 | _pin_ch2: PIN2, 106 | } 107 | 108 | // General-purpose timers (TIM2/TIM3/TIM4/TIM5) : Up, Down, Up/Down 109 | macro_rules! hal_qei { 110 | ($fct:ident,$TIMX:ty, $bits:ty) => { 111 | //, $CH1:ident<$AFCH1:ty>, $CH2:ident<$AFCH2:ty>) => { 112 | impl Qei { 113 | //Qei<$CH1>, $CH2>, $TIM> { 114 | pub fn $fct( 115 | tim: $TIMX, 116 | pin_ch1: PIN1, //$CH1>, 117 | pin_ch2: PIN2, //$CH2>, 118 | apb1: &mut APB1, 119 | options: QeiOptions, 120 | ) -> Self { 121 | // enable and reset peripheral to a clean slate state 122 | <$TIMX>::enable(apb1); 123 | <$TIMX>::reset(apb1); 124 | 125 | // Configure TxC1 and TxC2 as captures 126 | tim.ccmr1_output() 127 | .write(|w| unsafe { w.cc1s().bits(0b01).cc2s().bits(0b01) }); 128 | 129 | // enable and configure to capture on rising edge 130 | tim.ccer.write(|w| { 131 | w.cc1e() 132 | .set_bit() 133 | .cc1p() 134 | .clear_bit() 135 | .cc2e() 136 | .set_bit() 137 | .cc2p() 138 | .clear_bit() 139 | }); 140 | 141 | // configure as quadrature encoder 142 | tim.smcr.write(|w| w.sms().bits(options.slave_mode as u8)); 143 | tim.arr 144 | .write(|w| unsafe { w.bits(options.auto_reload_value) }); 145 | tim.cr1.write(|w| w.cen().set_bit()); 146 | 147 | Self { 148 | tim, 149 | _pin_ch1: pin_ch1, 150 | _pin_ch2: pin_ch2, 151 | } 152 | } 153 | 154 | pub fn read_count(&self) -> $bits { 155 | self.tim.cnt.read().bits() as $bits 156 | } 157 | 158 | pub fn read_direction(&self) -> Direction { 159 | if self.tim.cr1.read().dir().bit_is_clear() { 160 | Direction::Upcounting 161 | } else { 162 | Direction::Downcounting 163 | } 164 | } 165 | 166 | pub fn release(self) -> ($TIMX, PIN1, PIN2) { 167 | (self.tim, self._pin_ch1, self._pin_ch2) 168 | } 169 | } 170 | }; 171 | } 172 | 173 | hal_qei! {qei_tim2, TIM2, u32} 174 | hal_qei! {qei_tim3, TIM3, u16} 175 | hal_qei! {qei_tim4, TIM4, u16} 176 | hal_qei! {qei_tim5, TIM5, u32} 177 | -------------------------------------------------------------------------------- /src/rcc/enable.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | macro_rules! bus_enable { 4 | ($PER:ident => $en:ident) => { 5 | impl Enable for crate::pac::$PER { 6 | #[inline(always)] 7 | fn enable(bus: &mut Self::Bus) { 8 | bus.enr().modify(|_, w| w.$en().set_bit()); 9 | // Stall the pipeline to work around erratum 2.1.13 (DM00037591) 10 | cortex_m::asm::dsb(); 11 | } 12 | #[inline(always)] 13 | fn disable(bus: &mut Self::Bus) { 14 | bus.enr().modify(|_, w| w.$en().clear_bit()); 15 | } 16 | #[inline(always)] 17 | fn is_enabled() -> bool { 18 | Self::Bus::new().enr().read().$en().bit_is_set() 19 | } 20 | #[inline(always)] 21 | fn is_disabled() -> bool { 22 | Self::Bus::new().enr().read().$en().bit_is_clear() 23 | } 24 | #[inline(always)] 25 | unsafe fn enable_unchecked() { 26 | Self::enable(&mut Self::Bus::new()); 27 | } 28 | #[inline(always)] 29 | unsafe fn disable_unchecked() { 30 | Self::disable(&mut Self::Bus::new()); 31 | } 32 | } 33 | }; 34 | } 35 | 36 | macro_rules! bus_lpenable { 37 | ($PER:ident => $lpen:ident) => { 38 | impl LPEnable for crate::pac::$PER { 39 | #[inline(always)] 40 | fn low_power_enable(bus: &mut Self::Bus) { 41 | bus.lpenr().modify(|_, w| w.$lpen().set_bit()); 42 | // Stall the pipeline to work around erratum 2.1.13 (DM00037591) 43 | cortex_m::asm::dsb(); 44 | } 45 | #[inline(always)] 46 | fn low_power_disable(bus: &mut Self::Bus) { 47 | bus.lpenr().modify(|_, w| w.$lpen().clear_bit()); 48 | } 49 | #[inline(always)] 50 | fn is_low_power_enabled() -> bool { 51 | Self::Bus::new().lpenr().read().$lpen().bit_is_set() 52 | } 53 | #[inline(always)] 54 | fn is_low_power_disabled() -> bool { 55 | Self::Bus::new().lpenr().read().$lpen().bit_is_clear() 56 | } 57 | #[inline(always)] 58 | unsafe fn low_power_enable_unchecked() { 59 | Self::enable(&mut Self::Bus::new()); 60 | } 61 | #[inline(always)] 62 | unsafe fn low_power_disable_unchecked() { 63 | Self::disable(&mut Self::Bus::new()); 64 | } 65 | } 66 | }; 67 | } 68 | macro_rules! bus_reset { 69 | ($PER:ident => $rst:ident) => { 70 | impl Reset for crate::pac::$PER { 71 | #[inline(always)] 72 | fn reset(bus: &mut Self::Bus) { 73 | bus.rstr().modify(|_, w| w.$rst().set_bit()); 74 | bus.rstr().modify(|_, w| w.$rst().clear_bit()); 75 | } 76 | #[inline(always)] 77 | unsafe fn reset_unchecked() { 78 | Self::reset(&mut Self::Bus::new()); 79 | } 80 | } 81 | }; 82 | } 83 | 84 | macro_rules! bus { 85 | ($($PER:ident => ($busX:ty, $($en:ident)?, $($lpen:ident)?, $($rst:ident)?),)+) => { 86 | $( 87 | impl crate::Sealed for crate::pac::$PER {} 88 | impl RccBus for crate::pac::$PER { 89 | type Bus = $busX; 90 | } 91 | $(bus_enable!($PER => $en);)? 92 | $(bus_lpenable!($PER => $lpen);)? 93 | $(bus_reset!($PER => $rst);)? 94 | )+ 95 | }; 96 | } 97 | 98 | // Peripherals respective buses 99 | // TODO: check which processor has which peripheral and add them 100 | bus! { 101 | GPIOA => (AHB1, gpioaen, gpioalpen, gpioarst), // 0 102 | GPIOB => (AHB1, gpioben, gpioblpen, gpiobrst), // 1 103 | GPIOC => (AHB1, gpiocen, gpioclpen, gpiocrst), // 2 104 | GPIOD => (AHB1, gpioden, gpiodlpen, gpiodrst), // 3 105 | GPIOE => (AHB1, gpioeen, gpioelpen, gpioerst), // 4 106 | GPIOF => (AHB1, gpiofen, gpioflpen, gpiofrst), // 5 107 | GPIOG => (AHB1, gpiogen, gpioglpen, gpiogrst), // 6 108 | GPIOH => (AHB1, gpiohen, gpiohlpen, gpiohrst), // 7 109 | GPIOI => (AHB1, gpioien, gpioilpen, gpioirst), // 8 110 | CRC => (AHB1, crcen, crclpen, crcrst), // 12 111 | DMA1 => (AHB1, dma1en, dma1lpen, dma1rst), // 21 112 | DMA2 => (AHB1, dma2en, dma2lpen, dma2rst), // 22 113 | OTG_HS_GLOBAL => (AHB1, otghsen, otghslpen, otghsrst), // 29 114 | 115 | RNG => (AHB2, rngen, rnglpen, rngrst), // 6 116 | OTG_FS_GLOBAL => (AHB2, otgfsen, otgfslpen, otgfsrst), // 7 117 | 118 | FMC => (AHB3, fmcen, fmclpen, fmcrst), // 0 119 | QUADSPI => (AHB3, qspien, qspilpen, qspirst), // 1 120 | 121 | TIM2 => (APB1, tim2en, tim2lpen, tim2rst), // 0 122 | TIM3 => (APB1, tim3en, tim3lpen, tim3rst), // 1 123 | TIM4 => (APB1, tim4en, tim4lpen, tim4rst), // 2 124 | TIM5 => (APB1, tim5en, tim5lpen, tim5rst), // 3 125 | TIM6 => (APB1, tim6en, tim6lpen, tim6rst), // 4 126 | TIM7 => (APB1, tim7en, tim7lpen, tim7rst), // 5 127 | TIM12 => (APB1, tim12en, tim12lpen, tim12rst), // 6 128 | TIM13 => (APB1, tim13en, tim13lpen, tim13rst), // 7 129 | TIM14 => (APB1, tim14en, tim14lpen, tim14rst), // 8 130 | LPTIM1 => (APB1, lptim1en, lptim1lpen, lptim1rst), // 9 131 | WWDG => (APB1, wwdgen, wwdglpen, wwdgrst), // 11 132 | SPI2 => (APB1, spi2en, spi2lpen, spi2rst), // 14 133 | SPI3 => (APB1, spi3en, spi3lpen, spi3rst), // 15 134 | USART2 => (APB1, usart2en, usart2lpen, usart2rst), // 17 135 | USART3 => (APB1, usart3en, usart3lpen, usart3rst), // 18 136 | UART4 => (APB1, uart4en, uart4lpen, uart4rst), // 19 137 | UART5 => (APB1, uart5en, uart5lpen, uart5rst), // 20 138 | I2C1 => (APB1, i2c1en, i2c1lpen, i2c1rst), // 21 139 | I2C2 => (APB1, i2c2en, i2c2lpen, i2c2rst), // 22 140 | I2C3 => (APB1, i2c3en, i2c3lpen, i2c3rst), // 23 141 | CAN1 => (APB1, can1en, can1lpen, can1rst), // 25 142 | PWR => (APB1, pwren, pwrlpen, pwrrst), // 28 143 | DAC => (APB1, dacen, daclpen, dacrst), // 29 144 | UART7 => (APB1, uart7en, uart7lpen, uart7rst), // 30 145 | UART8 => (APB1, uart8en, uart8lpen, uart8rst), // 31 146 | 147 | TIM1 => (APB2, tim1en, tim1lpen, tim1rst), // 0 148 | TIM8 => (APB2, tim8en, tim8lpen, tim8rst), // 1 149 | USART1 => (APB2, usart1en, usart1lpen, usart1rst), // 4 150 | USART6 => (APB2, usart6en, usart6lpen, usart6rst), // 5 151 | ADC1 => (APB2, adc1en, adc1lpen, adcrst), // 8 152 | ADC2 => (APB2, adc2en, adc2lpen, adcrst), // 9 153 | ADC3 => (APB2, adc3en, adc3lpen, adcrst), // 10 154 | SDMMC1 => (APB2, sdmmc1en, sdmmc1lpen, sdmmc1rst), // 11 155 | SPI1 => (APB2, spi1en, spi1lpen, spi1rst), // 12 156 | SPI4 => (APB2, spi4en, spi4lpen, spi4rst), // 13 157 | SYSCFG => (APB2, syscfgen, syscfglpen, syscfgrst), // 14 158 | TIM9 => (APB2, tim9en, tim9lpen, tim9rst), // 16 159 | TIM10 => (APB2, tim10en, tim10lpen, tim10rst), // 17 160 | TIM11 => (APB2, tim11en, tim11lpen, tim11rst), // 18 161 | SPI5 => (APB2, spi5en, spi5lpen, spi5rst), // 20 162 | SAI1 => (APB2, sai1en, sai1lpen, sai1rst), // 22 163 | SAI2 => (APB2, sai2en, sai2lpen, sai2rst), // 23 164 | } 165 | 166 | #[cfg(any(feature = "svd-f730", feature = "svd-f7x2", feature = "svd-f7x3",))] 167 | bus! { 168 | AES => (AHB2, aesen, aeslpen, aesrst), // 4 169 | 170 | SDMMC2 => (APB2, sdmmc2en, sdmmc2lpen, sdmmc2rst), // 7 171 | USBPHYC => (APB2, usbphycen,, usbphycrst), // 31 172 | } 173 | 174 | #[cfg(any( 175 | feature = "svd-f745", 176 | feature = "svd-f750", 177 | feature = "svd-f7x6", 178 | feature = "svd-f765", 179 | feature = "svd-f7x7", 180 | feature = "svd-f7x9", 181 | ))] 182 | bus! { 183 | GPIOJ => (AHB1, gpiojen, gpiojlpen, gpiojrst), // 9 184 | GPIOK => (AHB1, gpioken, gpioklpen, gpiokrst), // 10 185 | DMA2D => (AHB1, dma2den, dma2dlpen, dma2drst), // 23 186 | ETHERNET_MAC => (AHB1, ethmacen, ethmaclpen, ethmacrst), // 25 187 | 188 | DCMI => (AHB2, dcmien, dcmilpen, dcmirst), // 0 189 | CRYP => (AHB2, crypen, cryplpen, cryprst), // 4 190 | HASH => (AHB2, hashen, hashlpen,), // 5 191 | 192 | SPDIFRX => (APB1, spdifrxen, spdifrxlpen, spdifrxrst), // 16 193 | I2C4 => (APB1, i2c4en, i2c4lpen, i2c4rst), // 24 194 | CAN2 => (APB1, can2en, can2lpen, can2rst), // 26 195 | CEC => (APB1, cecen, ceclpen, cecrst), // 27 196 | 197 | SPI6 => (APB2, spi6en, spi6lpen, spi6rst), // 21 198 | LTDC => (APB2, ltdcen, ltdclpen, ltdcrst), // 26 199 | } 200 | 201 | #[cfg(any(feature = "svd-f765", feature = "svd-f7x7", feature = "svd-f7x9"))] 202 | bus! { 203 | JPEG => (AHB2, jpegen, jpeglpen,), // 1 204 | 205 | CAN3 => (APB1, can3en, can3lpen, can3rst), // 13 206 | 207 | DSI => (APB2, dsien, dsilpen, dsirst), // 27 208 | MDIOS => (APB2, mdioen, mdiolpen, mdiorst), // 30 209 | } 210 | 211 | #[cfg(any(feature = "svd-f7x9", feature = "svd-f7x9"))] 212 | bus! { 213 | DFSDM => (APB2, dfsdm1en, dfsdm1lpen, dfsdm1rst), // 29 214 | } 215 | 216 | #[cfg(feature = "svd-f765")] 217 | bus! { 218 | DFSDM1 => (APB2, dfsdm1en, dfsdm1lpen, dfsdm1rst), // 29 219 | } 220 | -------------------------------------------------------------------------------- /src/rng.rs: -------------------------------------------------------------------------------- 1 | //! Interface to the true random number generator 2 | 3 | use core::cmp; 4 | use core::mem; 5 | 6 | use crate::pac::{RCC, RNG}; 7 | use crate::rcc::{Enable, Reset}; 8 | use core::num::NonZeroU32; 9 | use core::ops::Shl; 10 | use embedded_hal::blocking::rng::Read; 11 | use rand_core::RngCore; 12 | 13 | #[derive(Debug)] 14 | pub enum ErrorKind { 15 | /// The RNG_CLK was not correctly detected (fRNG_CLK< fHCLK/16). 16 | /// See CECS in RNG peripheral documentation. 17 | ClockError = 2, 18 | /// RNG detected more than 64 consecutive bits of the same value (0 or 1) OR 19 | /// more than 32 consecutive 01 pairs. 20 | /// See SECS in RNG peripheral documentation. 21 | SeedError = 4, 22 | } 23 | 24 | impl From for rand_core::Error { 25 | fn from(err: ErrorKind) -> rand_core::Error { 26 | let err_code = NonZeroU32::new(rand_core::Error::CUSTOM_START + err as u32).unwrap(); 27 | rand_core::Error::from(err_code) 28 | } 29 | } 30 | 31 | pub trait RngExt { 32 | fn init(self) -> Rng; 33 | } 34 | 35 | impl RngExt for RNG { 36 | /// Enable RNG_CLK and the RNG peripheral. 37 | /// Note that clocks must already be configured such that RNG_CLK is not less than 1/16 HCLK, 38 | /// otherwise all reads of the RNG would return a ClockError (CECS error). 39 | fn init(self) -> Rng { 40 | cortex_m::interrupt::free(|_| { 41 | let rcc = unsafe { &*RCC::ptr() }; 42 | 43 | // need set enable pll for this operation 44 | if rcc.cr.read().pllrdy().bit_is_clear() { 45 | rcc.cr.modify(|_, w| w.pllon().set_bit()); 46 | // wait till pll is ready 47 | while rcc.cr.read().pllrdy().bit_is_clear() {} 48 | } 49 | unsafe { 50 | // enable RNG_CLK (peripheral clock) 51 | RNG::enable_unchecked(); 52 | // give RNG_CLK time to start 53 | RNG::is_enabled(); 54 | // reset the RNG 55 | RNG::reset_unchecked(); 56 | } 57 | 58 | // enable the RNG peripheral 59 | self.cr.modify(|_, w| w.rngen().set_bit()); 60 | // hardware check for clock is used 61 | // instead of software calculation, which may be inaccurate. 62 | // until data is available we will check for CECS flag, if it is set 63 | // means that clock error occured 64 | while !self.sr.read().drdy().bit() { 65 | assert!(!self.sr.read().cecs().bit()); 66 | } 67 | }); 68 | 69 | Rng { rb: self } 70 | } 71 | } 72 | 73 | pub struct Rng { 74 | rb: RNG, 75 | } 76 | 77 | impl Rng { 78 | /// Returns 32 bits of random data from RNDATA, or error. 79 | /// May fail if, for example RNG_CLK is misconfigured. 80 | pub fn get_rand(&mut self) -> Result { 81 | loop { 82 | let status = self.rb.sr.read(); 83 | if status.cecs().bit() { 84 | return Err(ErrorKind::ClockError); 85 | } 86 | if status.secs().bit() { 87 | return Err(ErrorKind::SeedError); 88 | } 89 | if status.drdy().bit() { 90 | return Ok(self.rb.dr.read().rndata().bits()); 91 | } 92 | } 93 | } 94 | 95 | pub fn release(self) -> RNG { 96 | self.rb 97 | } 98 | } 99 | 100 | impl Read for Rng { 101 | type Error = rand_core::Error; 102 | 103 | fn read(&mut self, buffer: &mut [u8]) -> Result<(), Self::Error> { 104 | self.try_fill_bytes(buffer) 105 | } 106 | } 107 | 108 | impl RngCore for Rng { 109 | fn next_u32(&mut self) -> u32 { 110 | self.get_rand().unwrap() 111 | } 112 | 113 | fn next_u64(&mut self) -> u64 { 114 | let w1 = self.next_u32(); 115 | let w2 = self.next_u32(); 116 | (w1 as u64).shl(32) | (w2 as u64) 117 | } 118 | 119 | fn fill_bytes(&mut self, dest: &mut [u8]) { 120 | self.try_fill_bytes(dest).unwrap() 121 | } 122 | 123 | /// Fills buffer with random values, or returns an error 124 | fn try_fill_bytes(&mut self, buffer: &mut [u8]) -> Result<(), rand_core::Error> { 125 | const BATCH_SIZE: usize = 4 / mem::size_of::(); 126 | let mut i = 0_usize; 127 | while i < buffer.len() { 128 | let random_word = self.get_rand()?; 129 | let bytes = random_word.to_ne_bytes(); 130 | let n = cmp::min(BATCH_SIZE, buffer.len() - i); 131 | buffer[i..i + n].copy_from_slice(&bytes[..n]); 132 | i += n; 133 | } 134 | Ok(()) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/signature.rs: -------------------------------------------------------------------------------- 1 | //! Device electronic signature 2 | //! 3 | //! (stored in flash memory) 4 | 5 | use core::str::from_utf8_unchecked; 6 | 7 | /// This is the test voltage, in millivolts of the calibration done at the factory 8 | pub const VDDA_CALIB: u32 = 3300; 9 | 10 | macro_rules! define_ptr_type { 11 | ($name: ident, $ptr: expr) => { 12 | impl $name { 13 | fn ptr() -> *const Self { 14 | $ptr as *const _ 15 | } 16 | 17 | /// Returns a wrapped reference to the value in flash memory 18 | pub fn get() -> &'static Self { 19 | unsafe { &*Self::ptr() } 20 | } 21 | } 22 | }; 23 | } 24 | 25 | /// Uniqure Device ID register 26 | #[derive(Hash, Debug)] 27 | #[repr(C)] 28 | pub struct Uid { 29 | x: u16, 30 | y: u16, 31 | waf_lot: [u8; 8], 32 | } 33 | #[cfg(any(feature = "svd-f7x2", feature = "svd-f7x3", feature = "svd-f730"))] 34 | define_ptr_type!(Uid, 0x1FF0_7A10); 35 | #[cfg(not(any(feature = "svd-f7x2", feature = "svd-f7x3", feature = "svd-f730")))] 36 | define_ptr_type!(Uid, 0x1FF0_F420); 37 | 38 | impl Uid { 39 | /// X coordinate on wafer 40 | pub fn x(&self) -> u16 { 41 | self.x 42 | } 43 | 44 | /// Y coordinate on wafer 45 | pub fn y(&self) -> u16 { 46 | self.y 47 | } 48 | 49 | /// Wafer number 50 | pub fn waf_num(&self) -> u8 { 51 | self.waf_lot[0] 52 | } 53 | 54 | /// Lot number 55 | pub fn lot_num(&self) -> &str { 56 | unsafe { from_utf8_unchecked(&self.waf_lot[1..]) } 57 | } 58 | } 59 | 60 | /// Size of integrated flash 61 | #[derive(Debug)] 62 | #[repr(C)] 63 | pub struct FlashSize(u16); 64 | #[cfg(any(feature = "svd-f7x2", feature = "svd-f7x3", feature = "svd-f730"))] 65 | define_ptr_type!(FlashSize, 0x1FF0_7A22); 66 | #[cfg(not(any(feature = "svd-f7x2", feature = "svd-f7x3", feature = "svd-f730")))] 67 | define_ptr_type!(FlashSize, 0x1FF0_F442); 68 | 69 | impl FlashSize { 70 | /// Read flash size in kilobytes 71 | pub fn kilo_bytes(&self) -> u16 { 72 | self.0 73 | } 74 | 75 | /// Read flash size in bytes 76 | pub fn bytes(&self) -> usize { 77 | usize::from(self.kilo_bytes()) * 1024 78 | } 79 | } 80 | 81 | /// ADC VREF calibration value is stored in at the factory 82 | #[derive(Debug)] 83 | #[repr(C)] 84 | pub struct VrefCal(u16); 85 | #[cfg(any(feature = "svd-f7x2", feature = "svd-f7x3", feature = "svd-f730"))] 86 | define_ptr_type!(VrefCal, 0x1FF0_7A2A); 87 | #[cfg(not(any(feature = "svd-f7x2", feature = "svd-f7x3", feature = "svd-f730")))] 88 | define_ptr_type!(VrefCal, 0x1FF0_F44A); 89 | 90 | impl VrefCal { 91 | /// Read calibration value 92 | pub fn read(&self) -> u16 { 93 | self.0 94 | } 95 | } 96 | 97 | /// A temperature reading taken at 30°C stored at the factory 98 | #[derive(Debug)] 99 | #[repr(C)] 100 | pub struct VtempCal30(u16); 101 | #[cfg(any(feature = "svd-f7x2", feature = "svd-f7x3", feature = "svd-f730"))] 102 | define_ptr_type!(VtempCal30, 0x1FF0_7A2C); 103 | #[cfg(not(any(feature = "svd-f7x2", feature = "svd-f7x3", feature = "svd-f730")))] 104 | define_ptr_type!(VtempCal30, 0x1FF0_F44C); 105 | 106 | impl VtempCal30 { 107 | /// Read calibration value 108 | pub fn read(&self) -> u16 { 109 | self.0 110 | } 111 | } 112 | 113 | /// A temperature reading taken at 110°C stored at the factory 114 | #[derive(Debug)] 115 | #[repr(C)] 116 | pub struct VtempCal110(u16); 117 | #[cfg(any(feature = "svd-f7x2", feature = "svd-f7x3", feature = "svd-f730"))] 118 | define_ptr_type!(VtempCal110, 0x1FF0_7A2E); 119 | #[cfg(not(any(feature = "svd-f7x2", feature = "svd-f7x3", feature = "svd-f730")))] 120 | define_ptr_type!(VtempCal110, 0x1FF0_F44E); 121 | 122 | impl VtempCal110 { 123 | /// Read calibration value 124 | pub fn read(&self) -> u16 { 125 | self.0 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/timer/counter.rs: -------------------------------------------------------------------------------- 1 | use super::{compute_arr_presc, Error, Event, FTimer, Instance, SysEvent, Timer}; 2 | use crate::pac::SYST; 3 | use core::ops::{Deref, DerefMut}; 4 | use fugit::{HertzU32 as Hertz, TimerDurationU32, TimerInstantU32}; 5 | 6 | /// Hardware timers 7 | pub struct CounterHz(pub(super) Timer); 8 | 9 | impl Deref for CounterHz { 10 | type Target = Timer; 11 | fn deref(&self) -> &Self::Target { 12 | &self.0 13 | } 14 | } 15 | 16 | impl DerefMut for CounterHz { 17 | fn deref_mut(&mut self) -> &mut Self::Target { 18 | &mut self.0 19 | } 20 | } 21 | 22 | impl CounterHz { 23 | /// Releases the TIM peripheral 24 | pub fn release(mut self) -> Timer { 25 | // stop timer 26 | self.tim.cr1_reset(); 27 | self.0 28 | } 29 | } 30 | 31 | impl CounterHz { 32 | pub fn start(&mut self, timeout: Hertz) -> Result<(), Error> { 33 | // pause 34 | self.tim.disable_counter(); 35 | // reset counter 36 | self.tim.reset_counter(); 37 | 38 | let (psc, arr) = compute_arr_presc(timeout.raw(), self.clk.raw()); 39 | self.tim.set_prescaler(psc); 40 | self.tim.set_auto_reload(arr)?; 41 | 42 | // Trigger update event to load the registers 43 | self.tim.trigger_update(); 44 | 45 | // start counter 46 | self.tim.enable_counter(); 47 | 48 | Ok(()) 49 | } 50 | 51 | pub fn wait(&mut self) -> nb::Result<(), Error> { 52 | if self.tim.get_interrupt_flag().contains(Event::Update) { 53 | self.tim.clear_interrupt_flag(Event::Update); 54 | Ok(()) 55 | } else { 56 | Err(nb::Error::WouldBlock) 57 | } 58 | } 59 | 60 | pub fn cancel(&mut self) -> Result<(), Error> { 61 | if !self.tim.is_counter_enabled() { 62 | return Err(Error::Disabled); 63 | } 64 | 65 | // disable counter 66 | self.tim.disable_counter(); 67 | Ok(()) 68 | } 69 | } 70 | 71 | /// Periodic non-blocking timer that imlements [embedded_hal::timer::CountDown] 72 | pub struct Counter(pub(super) FTimer); 73 | 74 | impl Deref for Counter { 75 | type Target = FTimer; 76 | fn deref(&self) -> &Self::Target { 77 | &self.0 78 | } 79 | } 80 | 81 | impl DerefMut for Counter { 82 | fn deref_mut(&mut self) -> &mut Self::Target { 83 | &mut self.0 84 | } 85 | } 86 | 87 | /// `Counter` with precision of 1 μs (1 MHz sampling) 88 | pub type CounterUs = Counter; 89 | 90 | /// `Counter` with precision of of 1 ms (1 kHz sampling) 91 | /// 92 | /// NOTE: don't use this if your system frequency more than 65 MHz 93 | pub type CounterMs = Counter; 94 | 95 | impl Counter { 96 | /// Releases the TIM peripheral 97 | pub fn release(mut self) -> FTimer { 98 | // stop counter 99 | self.tim.cr1_reset(); 100 | self.0 101 | } 102 | 103 | pub fn now(&self) -> TimerInstantU32 { 104 | TimerInstantU32::from_ticks(self.tim.read_count().into()) 105 | } 106 | 107 | pub fn start(&mut self, timeout: TimerDurationU32) -> Result<(), Error> { 108 | // pause 109 | self.tim.disable_counter(); 110 | // reset counter 111 | self.tim.reset_counter(); 112 | 113 | self.tim.set_auto_reload(timeout.ticks() - 1)?; 114 | 115 | // Trigger update event to load the registers 116 | self.tim.trigger_update(); 117 | 118 | // start counter 119 | self.tim.enable_counter(); 120 | 121 | Ok(()) 122 | } 123 | 124 | pub fn wait(&mut self) -> nb::Result<(), Error> { 125 | if self.tim.get_interrupt_flag().contains(Event::Update) { 126 | self.tim.clear_interrupt_flag(Event::Update); 127 | Ok(()) 128 | } else { 129 | Err(nb::Error::WouldBlock) 130 | } 131 | } 132 | 133 | pub fn cancel(&mut self) -> Result<(), Error> { 134 | if !self.tim.is_counter_enabled() { 135 | return Err(Error::Disabled); 136 | } 137 | 138 | // disable counter 139 | self.tim.disable_counter(); 140 | Ok(()) 141 | } 142 | } 143 | 144 | impl fugit_timer::Timer for Counter { 145 | type Error = Error; 146 | 147 | fn now(&mut self) -> TimerInstantU32 { 148 | Self::now(self) 149 | } 150 | 151 | fn start(&mut self, duration: TimerDurationU32) -> Result<(), Self::Error> { 152 | self.start(duration) 153 | } 154 | 155 | fn cancel(&mut self) -> Result<(), Self::Error> { 156 | self.cancel() 157 | } 158 | 159 | fn wait(&mut self) -> nb::Result<(), Self::Error> { 160 | self.wait() 161 | } 162 | } 163 | 164 | impl Timer { 165 | /// Creates [SysCounterHz] which takes [Hertz] as Duration 166 | pub fn counter_hz(self) -> SysCounterHz { 167 | SysCounterHz(self) 168 | } 169 | 170 | /// Creates [SysCounter] with custom precision (core frequency recommended is known) 171 | pub fn counter(self) -> SysCounter { 172 | SysCounter(self) 173 | } 174 | 175 | /// Creates [SysCounter] 1 microsecond precision 176 | pub fn counter_us(self) -> SysCounterUs { 177 | SysCounter(self) 178 | } 179 | } 180 | 181 | /// Hardware timers 182 | pub struct SysCounterHz(Timer); 183 | 184 | impl Deref for SysCounterHz { 185 | type Target = Timer; 186 | fn deref(&self) -> &Self::Target { 187 | &self.0 188 | } 189 | } 190 | 191 | impl DerefMut for SysCounterHz { 192 | fn deref_mut(&mut self) -> &mut Self::Target { 193 | &mut self.0 194 | } 195 | } 196 | 197 | impl SysCounterHz { 198 | pub fn start(&mut self, timeout: Hertz) -> Result<(), Error> { 199 | let rvr = self.clk.raw() / timeout.raw() - 1; 200 | 201 | if rvr >= (1 << 24) { 202 | return Err(Error::WrongAutoReload); 203 | } 204 | 205 | self.tim.set_reload(rvr); 206 | self.tim.clear_current(); 207 | self.tim.enable_counter(); 208 | 209 | Ok(()) 210 | } 211 | 212 | pub fn wait(&mut self) -> nb::Result<(), Error> { 213 | if self.tim.has_wrapped() { 214 | Ok(()) 215 | } else { 216 | Err(nb::Error::WouldBlock) 217 | } 218 | } 219 | 220 | pub fn cancel(&mut self) -> Result<(), Error> { 221 | if !self.tim.is_counter_enabled() { 222 | return Err(Error::Disabled); 223 | } 224 | 225 | self.tim.disable_counter(); 226 | Ok(()) 227 | } 228 | } 229 | 230 | pub type SysCounterUs = SysCounter<1_000_000>; 231 | 232 | /// SysTick timer with precision of 1 μs (1 MHz sampling) 233 | pub struct SysCounter(Timer); 234 | 235 | impl Deref for SysCounter { 236 | type Target = Timer; 237 | fn deref(&self) -> &Self::Target { 238 | &self.0 239 | } 240 | } 241 | 242 | impl DerefMut for SysCounter { 243 | fn deref_mut(&mut self) -> &mut Self::Target { 244 | &mut self.0 245 | } 246 | } 247 | 248 | impl SysCounter { 249 | /// Starts listening for an `event` 250 | pub fn listen(&mut self, event: SysEvent) { 251 | match event { 252 | SysEvent::Update => self.tim.enable_interrupt(), 253 | } 254 | } 255 | 256 | /// Stops listening for an `event` 257 | pub fn unlisten(&mut self, event: SysEvent) { 258 | match event { 259 | SysEvent::Update => self.tim.disable_interrupt(), 260 | } 261 | } 262 | 263 | pub fn now(&self) -> TimerInstantU32 { 264 | TimerInstantU32::from_ticks( 265 | (SYST::get_reload() - SYST::get_current()) / (self.clk.raw() / FREQ), 266 | ) 267 | } 268 | 269 | pub fn start(&mut self, timeout: TimerDurationU32) -> Result<(), Error> { 270 | let rvr = timeout.ticks() * (self.clk.raw() / FREQ) - 1; 271 | 272 | if rvr >= (1 << 24) { 273 | return Err(Error::WrongAutoReload); 274 | } 275 | 276 | self.tim.set_reload(rvr); 277 | self.tim.clear_current(); 278 | self.tim.enable_counter(); 279 | 280 | Ok(()) 281 | } 282 | 283 | pub fn wait(&mut self) -> nb::Result<(), Error> { 284 | if self.tim.has_wrapped() { 285 | Ok(()) 286 | } else { 287 | Err(nb::Error::WouldBlock) 288 | } 289 | } 290 | 291 | pub fn cancel(&mut self) -> Result<(), Error> { 292 | if !self.tim.is_counter_enabled() { 293 | return Err(Error::Disabled); 294 | } 295 | 296 | self.tim.disable_counter(); 297 | Ok(()) 298 | } 299 | } 300 | 301 | impl fugit_timer::Timer for SysCounter { 302 | type Error = Error; 303 | 304 | fn now(&mut self) -> TimerInstantU32 { 305 | Self::now(self) 306 | } 307 | 308 | fn start(&mut self, duration: TimerDurationU32) -> Result<(), Self::Error> { 309 | self.start(duration) 310 | } 311 | 312 | fn wait(&mut self) -> nb::Result<(), Self::Error> { 313 | self.wait() 314 | } 315 | 316 | fn cancel(&mut self) -> Result<(), Self::Error> { 317 | self.cancel() 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /src/timer/delay.rs: -------------------------------------------------------------------------------- 1 | //! Delays 2 | 3 | use super::{FTimer, Instance, Timer}; 4 | use core::ops::{Deref, DerefMut}; 5 | use cortex_m::peripheral::SYST; 6 | use fugit::{MicrosDurationU32, TimerDurationU32}; 7 | 8 | /// Timer as a delay provider (SysTick by default) 9 | pub struct SysDelay(Timer); 10 | 11 | impl Deref for SysDelay { 12 | type Target = Timer; 13 | fn deref(&self) -> &Self::Target { 14 | &self.0 15 | } 16 | } 17 | 18 | impl DerefMut for SysDelay { 19 | fn deref_mut(&mut self) -> &mut Self::Target { 20 | &mut self.0 21 | } 22 | } 23 | 24 | impl SysDelay { 25 | /// Releases the timer resource 26 | pub fn release(self) -> Timer { 27 | self.0 28 | } 29 | } 30 | 31 | impl Timer { 32 | pub fn delay(self) -> SysDelay { 33 | SysDelay(self) 34 | } 35 | } 36 | 37 | impl SysDelay { 38 | pub fn delay(&mut self, us: MicrosDurationU32) { 39 | // The SysTick Reload Value register supports values between 1 and 0x00FFFFFF. 40 | const MAX_RVR: u32 = 0x00FF_FFFF; 41 | 42 | let mut total_rvr = us.ticks() * (self.clk.raw() / 1_000_000); 43 | 44 | while total_rvr != 0 { 45 | let current_rvr = total_rvr.min(MAX_RVR); 46 | 47 | self.tim.set_reload(current_rvr); 48 | self.tim.clear_current(); 49 | self.tim.enable_counter(); 50 | 51 | // Update the tracking variable while we are waiting... 52 | total_rvr -= current_rvr; 53 | 54 | while !self.tim.has_wrapped() {} 55 | 56 | self.tim.disable_counter(); 57 | } 58 | } 59 | } 60 | 61 | /// Periodic non-blocking timer that imlements [embedded_hal::blocking::delay] traits 62 | pub struct Delay(pub(super) FTimer); 63 | 64 | impl Deref for Delay { 65 | type Target = FTimer; 66 | fn deref(&self) -> &Self::Target { 67 | &self.0 68 | } 69 | } 70 | 71 | impl DerefMut for Delay { 72 | fn deref_mut(&mut self) -> &mut Self::Target { 73 | &mut self.0 74 | } 75 | } 76 | 77 | /// `Delay` with precision of 1 μs (1 MHz sampling) 78 | pub type DelayUs = Delay; 79 | 80 | /// `Delay` with precision of 1 ms (1 kHz sampling) 81 | /// 82 | /// NOTE: don't use this if your system frequency more than 65 MHz 83 | pub type DelayMs = Delay; 84 | 85 | impl Delay { 86 | /// Sleep for given time 87 | pub fn delay(&mut self, time: TimerDurationU32) { 88 | let mut ticks = time.ticks().max(1) - 1; 89 | while ticks != 0 { 90 | let reload = ticks.min(TIM::max_auto_reload()); 91 | 92 | // Write Auto-Reload Register (ARR) 93 | unsafe { 94 | self.tim.set_auto_reload_unchecked(reload); 95 | } 96 | 97 | // Trigger update event (UEV) in the event generation register (EGR) 98 | // in order to immediately apply the config 99 | self.tim.trigger_update(); 100 | 101 | // Configure the counter in one-pulse mode (counter stops counting at 102 | // the next updateevent, clearing the CEN bit) and enable the counter. 103 | self.tim.start_one_pulse(); 104 | 105 | // Update the tracking variable while we are waiting... 106 | ticks -= reload; 107 | // Wait for CEN bit to clear 108 | while self.tim.is_counter_enabled() { /* wait */ } 109 | } 110 | } 111 | 112 | pub fn max_delay(&self) -> TimerDurationU32 { 113 | TimerDurationU32::from_ticks(TIM::max_auto_reload()) 114 | } 115 | 116 | /// Releases the TIM peripheral 117 | pub fn release(mut self) -> FTimer { 118 | // stop counter 119 | self.tim.cr1_reset(); 120 | self.0 121 | } 122 | } 123 | 124 | impl fugit_timer::Delay for Delay { 125 | type Error = core::convert::Infallible; 126 | 127 | fn delay(&mut self, duration: TimerDurationU32) -> Result<(), Self::Error> { 128 | self.delay(duration); 129 | Ok(()) 130 | } 131 | } 132 | 133 | impl fugit_timer::Delay<1_000_000> for SysDelay { 134 | type Error = core::convert::Infallible; 135 | 136 | fn delay(&mut self, duration: MicrosDurationU32) -> Result<(), Self::Error> { 137 | self.delay(duration); 138 | Ok(()) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/timer/hal_02.rs: -------------------------------------------------------------------------------- 1 | //! Delay implementation based on general-purpose 32 bit timers and System timer (SysTick). 2 | //! 3 | //! TIM2 and TIM5 are a general purpose 32-bit auto-reload up/downcounter with 4 | //! a 16-bit prescaler. 5 | 6 | use embedded_hal::{ 7 | blocking::delay::{DelayMs, DelayUs}, 8 | timer::{Cancel, CountDown, Periodic}, 9 | }; 10 | use fugit::{ExtU32, HertzU32 as Hertz, TimerDurationU32}; 11 | use void::Void; 12 | 13 | use super::{ 14 | Channel, Counter, CounterHz, Delay, Error, Instance, Pins, Pwm, PwmChannel, PwmHz, SysCounter, 15 | SysCounterHz, SysDelay, WithPwm, 16 | }; 17 | 18 | impl DelayUs for SysDelay { 19 | fn delay_us(&mut self, us: u32) { 20 | self.delay(us.micros()) 21 | } 22 | } 23 | 24 | impl DelayMs for SysDelay { 25 | fn delay_ms(&mut self, ms: u32) { 26 | self.delay_us(ms * 1_000); 27 | } 28 | } 29 | 30 | impl DelayUs for SysDelay { 31 | fn delay_us(&mut self, us: u16) { 32 | self.delay_us(us as u32) 33 | } 34 | } 35 | 36 | impl DelayMs for SysDelay { 37 | fn delay_ms(&mut self, ms: u16) { 38 | self.delay_ms(ms as u32); 39 | } 40 | } 41 | 42 | impl DelayUs for SysDelay { 43 | fn delay_us(&mut self, us: u8) { 44 | self.delay_us(us as u32) 45 | } 46 | } 47 | 48 | impl DelayMs for SysDelay { 49 | fn delay_ms(&mut self, ms: u8) { 50 | self.delay_ms(ms as u32); 51 | } 52 | } 53 | 54 | impl Periodic for CounterHz {} 55 | impl Periodic for SysCounterHz {} 56 | impl Periodic for SysCounter {} 57 | 58 | impl CountDown for SysCounterHz { 59 | type Time = Hertz; 60 | 61 | fn start(&mut self, timeout: T) 62 | where 63 | T: Into, 64 | { 65 | self.start(timeout.into()).unwrap() 66 | } 67 | 68 | fn wait(&mut self) -> nb::Result<(), Void> { 69 | match self.wait() { 70 | Err(nb::Error::WouldBlock) => Err(nb::Error::WouldBlock), 71 | _ => Ok(()), 72 | } 73 | } 74 | } 75 | 76 | impl Cancel for SysCounterHz { 77 | type Error = Error; 78 | 79 | fn cancel(&mut self) -> Result<(), Self::Error> { 80 | self.cancel() 81 | } 82 | } 83 | 84 | impl CountDown for CounterHz { 85 | type Time = Hertz; 86 | 87 | fn start(&mut self, timeout: T) 88 | where 89 | T: Into, 90 | { 91 | self.start(timeout.into()).unwrap() 92 | } 93 | 94 | fn wait(&mut self) -> nb::Result<(), Void> { 95 | match self.wait() { 96 | Err(nb::Error::WouldBlock) => Err(nb::Error::WouldBlock), 97 | _ => Ok(()), 98 | } 99 | } 100 | } 101 | 102 | impl Cancel for CounterHz { 103 | type Error = Error; 104 | 105 | fn cancel(&mut self) -> Result<(), Self::Error> { 106 | self.cancel() 107 | } 108 | } 109 | 110 | impl CountDown for SysCounter { 111 | type Time = TimerDurationU32; 112 | 113 | fn start(&mut self, timeout: T) 114 | where 115 | T: Into, 116 | { 117 | self.start(timeout.into()).unwrap() 118 | } 119 | 120 | fn wait(&mut self) -> nb::Result<(), Void> { 121 | match self.wait() { 122 | Err(nb::Error::WouldBlock) => Err(nb::Error::WouldBlock), 123 | _ => Ok(()), 124 | } 125 | } 126 | } 127 | 128 | impl Cancel for SysCounter { 129 | type Error = Error; 130 | 131 | fn cancel(&mut self) -> Result<(), Self::Error> { 132 | self.cancel() 133 | } 134 | } 135 | 136 | impl embedded_hal::PwmPin for PwmChannel { 137 | type Duty = u16; 138 | 139 | fn disable(&mut self) { 140 | self.disable() 141 | } 142 | fn enable(&mut self) { 143 | self.enable() 144 | } 145 | fn get_duty(&self) -> Self::Duty { 146 | self.get_duty() 147 | } 148 | fn get_max_duty(&self) -> Self::Duty { 149 | self.get_max_duty() 150 | } 151 | fn set_duty(&mut self, duty: Self::Duty) { 152 | self.set_duty(duty) 153 | } 154 | } 155 | 156 | impl embedded_hal::Pwm for PwmHz 157 | where 158 | TIM: Instance + WithPwm, 159 | PINS: Pins, 160 | { 161 | type Channel = Channel; 162 | type Duty = u16; 163 | type Time = Hertz; 164 | 165 | fn enable(&mut self, channel: Self::Channel) { 166 | self.enable(channel) 167 | } 168 | 169 | fn disable(&mut self, channel: Self::Channel) { 170 | self.disable(channel) 171 | } 172 | 173 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { 174 | self.get_duty(channel) 175 | } 176 | 177 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { 178 | self.set_duty(channel, duty) 179 | } 180 | 181 | /// If `0` returned means max_duty is 2^16 182 | fn get_max_duty(&self) -> Self::Duty { 183 | self.get_max_duty() 184 | } 185 | 186 | fn get_period(&self) -> Self::Time { 187 | self.get_period() 188 | } 189 | 190 | fn set_period(&mut self, period: T) 191 | where 192 | T: Into, 193 | { 194 | self.set_period(period.into()) 195 | } 196 | } 197 | 198 | impl DelayUs for Delay { 199 | /// Sleep for `us` microseconds 200 | fn delay_us(&mut self, us: u32) { 201 | self.delay(us.micros()) 202 | } 203 | } 204 | 205 | impl DelayMs for Delay { 206 | /// Sleep for `ms` milliseconds 207 | fn delay_ms(&mut self, ms: u32) { 208 | self.delay(ms.millis()) 209 | } 210 | } 211 | 212 | impl DelayUs for Delay { 213 | /// Sleep for `us` microseconds 214 | fn delay_us(&mut self, us: u16) { 215 | self.delay((us as u32).micros()) 216 | } 217 | } 218 | impl DelayMs for Delay { 219 | /// Sleep for `ms` milliseconds 220 | fn delay_ms(&mut self, ms: u16) { 221 | self.delay((ms as u32).millis()) 222 | } 223 | } 224 | 225 | impl DelayUs for Delay { 226 | /// Sleep for `us` microseconds 227 | fn delay_us(&mut self, us: u8) { 228 | self.delay((us as u32).micros()) 229 | } 230 | } 231 | impl DelayMs for Delay { 232 | /// Sleep for `ms` milliseconds 233 | fn delay_ms(&mut self, ms: u8) { 234 | self.delay((ms as u32).millis()) 235 | } 236 | } 237 | 238 | impl Periodic for Counter {} 239 | 240 | impl CountDown for Counter { 241 | type Time = TimerDurationU32; 242 | 243 | fn start(&mut self, timeout: T) 244 | where 245 | T: Into, 246 | { 247 | self.start(timeout.into()).unwrap() 248 | } 249 | 250 | fn wait(&mut self) -> nb::Result<(), Void> { 251 | match self.wait() { 252 | Err(nb::Error::WouldBlock) => Err(nb::Error::WouldBlock), 253 | _ => Ok(()), 254 | } 255 | } 256 | } 257 | 258 | impl Cancel for Counter { 259 | type Error = Error; 260 | 261 | fn cancel(&mut self) -> Result<(), Self::Error> { 262 | self.cancel() 263 | } 264 | } 265 | 266 | impl embedded_hal::Pwm for Pwm 267 | where 268 | TIM: Instance + WithPwm, 269 | PINS: Pins, 270 | { 271 | type Channel = Channel; 272 | type Duty = u16; 273 | type Time = TimerDurationU32; 274 | 275 | fn enable(&mut self, channel: Self::Channel) { 276 | self.enable(channel) 277 | } 278 | 279 | fn disable(&mut self, channel: Self::Channel) { 280 | self.disable(channel) 281 | } 282 | 283 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { 284 | self.get_duty(channel) 285 | } 286 | 287 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { 288 | self.set_duty(channel, duty) 289 | } 290 | 291 | /// If `0` returned means max_duty is 2^16 292 | fn get_max_duty(&self) -> Self::Duty { 293 | self.get_max_duty() 294 | } 295 | 296 | fn get_period(&self) -> Self::Time { 297 | self.get_period() 298 | } 299 | 300 | fn set_period(&mut self, period: T) 301 | where 302 | T: Into, 303 | { 304 | self.set_period(period.into()) 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/timer/monotonic.rs: -------------------------------------------------------------------------------- 1 | // RTIC Monotonic impl for the 32-bit timers 2 | use super::{Channel, Event, FTimer, General, Instance, WithPwm}; 3 | use crate::rcc::Clocks; 4 | use core::ops::{Deref, DerefMut}; 5 | pub use fugit::{self, ExtU32}; 6 | use rtic_monotonic::Monotonic; 7 | 8 | pub struct MonoTimer(FTimer); 9 | 10 | impl Deref for MonoTimer { 11 | type Target = FTimer; 12 | fn deref(&self) -> &Self::Target { 13 | &self.0 14 | } 15 | } 16 | 17 | impl DerefMut for MonoTimer { 18 | fn deref_mut(&mut self) -> &mut Self::Target { 19 | &mut self.0 20 | } 21 | } 22 | 23 | /// `MonoTimer` with precision of 1 μs (1 MHz sampling) 24 | pub type MonoTimerUs = MonoTimer; 25 | 26 | impl MonoTimer { 27 | /// Releases the TIM peripheral 28 | pub fn release(mut self) -> FTimer { 29 | // stop counter 30 | self.tim.cr1_reset(); 31 | self.0 32 | } 33 | } 34 | 35 | pub trait MonoTimerExt: Sized { 36 | fn monotonic(self, clocks: &Clocks) -> MonoTimer; 37 | fn monotonic_us(self, clocks: &Clocks) -> MonoTimer { 38 | self.monotonic::<1_000_000>(clocks) 39 | } 40 | } 41 | 42 | impl MonoTimerExt for TIM 43 | where 44 | Self: Instance + General + WithPwm, 45 | { 46 | fn monotonic(self, clocks: &Clocks) -> MonoTimer { 47 | FTimer::new(self, clocks).monotonic() 48 | } 49 | } 50 | 51 | impl FTimer 52 | where 53 | TIM: Instance + General + WithPwm, 54 | { 55 | pub fn monotonic(mut self) -> MonoTimer { 56 | unsafe { 57 | self.tim.set_auto_reload_unchecked(TIM::max_auto_reload()); 58 | } 59 | self.tim.trigger_update(); 60 | self.tim.start_no_update(); 61 | MonoTimer(self) 62 | } 63 | } 64 | 65 | impl Monotonic for MonoTimer 66 | where 67 | TIM: Instance + General + WithPwm, 68 | { 69 | type Instant = fugit::TimerInstantU32; 70 | type Duration = fugit::TimerDurationU32; 71 | 72 | unsafe fn reset(&mut self) { 73 | self.tim.listen_interrupt(Event::C1, true); 74 | } 75 | 76 | #[inline(always)] 77 | fn now(&mut self) -> Self::Instant { 78 | Self::Instant::from_ticks(self.tim.read_count()) 79 | } 80 | 81 | fn set_compare(&mut self, instant: Self::Instant) { 82 | TIM::set_cc_value(Channel::C1 as u8, instant.duration_since_epoch().ticks()); 83 | } 84 | 85 | fn clear_compare_flag(&mut self) { 86 | self.tim.clear_interrupt_flag(Event::C1); 87 | } 88 | 89 | #[inline(always)] 90 | fn zero() -> Self::Instant { 91 | Self::Instant::from_ticks(0) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/timer/pins.rs: -------------------------------------------------------------------------------- 1 | use crate::gpio::{self, Alternate}; 2 | 3 | // Output channels markers 4 | pub trait CPin {} 5 | pub struct Ch; 6 | pub const C1: u8 = 0; 7 | pub const C2: u8 = 1; 8 | pub const C3: u8 = 2; 9 | pub const C4: u8 = 3; 10 | 11 | macro_rules! channel_impl { 12 | ( $( $TIM:ident, $C:ident, $PINX:ident, $AF:literal; )+ ) => { 13 | $( 14 | impl CPin for gpio::$PINX> { } 15 | )+ 16 | }; 17 | } 18 | 19 | // The approach to PWM channel implementation is to group parts with 20 | // common pins, starting with groupings of the largest number of parts 21 | // and moving to smaller and smaller groupings. Last, we have individual 22 | // parts to cover exceptions. 23 | 24 | // All parts have these PWM pins. 25 | channel_impl!( 26 | TIM1, C1, PA8, 1; 27 | TIM1, C2, PA9, 1; 28 | TIM1, C3, PA10, 1; 29 | TIM1, C4, PA11, 1; 30 | 31 | TIM5, C1, PA0, 2; 32 | TIM5, C2, PA1, 2; 33 | TIM5, C3, PA2, 2; 34 | TIM5, C4, PA3, 2; 35 | 36 | TIM9, C1, PA2, 3; 37 | TIM9, C2, PA3, 3; 38 | 39 | TIM11, C1, PB9, 3; 40 | ); 41 | 42 | // TODO: check for other groups of parts 43 | channel_impl!( 44 | TIM1, C1, PE9, 1; 45 | TIM1, C2, PE11, 1; 46 | TIM1, C3, PE13, 1; 47 | TIM1, C4, PE14, 1; 48 | 49 | TIM2, C1, PA0, 1; 50 | TIM2, C2, PA1, 1; 51 | TIM2, C3, PA2, 1; 52 | TIM2, C4, PA3, 1; 53 | 54 | TIM2, C2, PB3, 1; 55 | TIM2, C3, PB10, 1; 56 | TIM2, C4, PB11, 1; 57 | 58 | TIM2, C1, PA5, 1; 59 | TIM2, C1, PA15, 1; 60 | 61 | TIM3, C1, PA6, 2; 62 | TIM3, C2, PA7, 2; 63 | TIM3, C3, PB0, 2; 64 | TIM3, C4, PB1, 2; 65 | 66 | TIM3, C1, PB4, 2; 67 | TIM3, C2, PB5, 2; 68 | 69 | TIM3, C1, PC6, 2; 70 | TIM3, C2, PC7, 2; 71 | TIM3, C3, PC8, 2; 72 | TIM3, C4, PC9, 2; 73 | 74 | TIM4, C1, PB6, 2; 75 | TIM4, C2, PB7, 2; 76 | TIM4, C3, PB8, 2; 77 | TIM4, C4, PB9, 2; 78 | 79 | TIM4, C1, PD12, 2; 80 | TIM4, C2, PD13, 2; 81 | TIM4, C3, PD14, 2; 82 | TIM4, C4, PD15, 2; 83 | 84 | TIM10, C1, PB8, 3; 85 | ); 86 | 87 | channel_impl!( 88 | TIM9, C1, PE5, 3; 89 | TIM9, C2, PE6, 3; 90 | ); 91 | 92 | channel_impl!( 93 | TIM8, C1, PC6, 3; 94 | TIM8, C2, PC7, 3; 95 | TIM8, C3, PC8, 3; 96 | TIM8, C4, PC9, 3; 97 | 98 | TIM10, C1, PF6, 3; 99 | 100 | TIM11, C1, PF7, 3; 101 | 102 | TIM12, C1, PB14, 9; 103 | TIM12, C2, PB15, 9; 104 | 105 | TIM13, C1, PA6, 9; 106 | TIM13, C1, PF8, 9; // Not a mistake: TIM13 has only one channel. 107 | 108 | TIM14, C1, PA7, 9; 109 | TIM14, C1, PF9, 9; // Not a mistake: TIM14 has only one channel. 110 | ); 111 | 112 | channel_impl!( 113 | TIM5, C1, PH10, 2; 114 | TIM5, C2, PH11, 2; 115 | TIM5, C3, PH12, 2; 116 | TIM5, C4, PI0, 2; 117 | 118 | TIM8, C1, PI5, 3; 119 | TIM8, C2, PI6, 3; 120 | TIM8, C3, PI7, 3; 121 | TIM8, C4, PI2, 3; 122 | 123 | TIM12, C1, PH6, 9; 124 | TIM12, C2, PH9, 9; 125 | ); 126 | -------------------------------------------------------------------------------- /src/timer/pwm_input.rs: -------------------------------------------------------------------------------- 1 | use super::{CPin, General, Instance, Timer, WithPwm}; 2 | use core::convert::TryFrom; 3 | use core::ops::{Deref, DerefMut}; 4 | use fugit::HertzU32 as Hertz; 5 | 6 | pub trait Pins {} 7 | 8 | // implement the `Pins` trait wherever PC1 implements CPin 9 | impl Pins for PC1 where PC1: CPin {} 10 | 11 | /// Represents a TIMer configured as a PWM input. 12 | /// This peripheral will emit an interrupt on CC2 events, which occurs at two times in this mode: 13 | /// 1. When a new cycle is started: the duty cycle will be `1.00` 14 | /// 2. When the period is captured. the duty cycle will be an observable value. 15 | /// An example interrupt handler is provided: 16 | /// ``` 17 | /// use stm32f4xx_hal::pac::TIM8; 18 | /// use stm32f4xx_hal::timer::Timer; 19 | /// use stm32f4xx_hal::pwm_input::PwmInput; 20 | /// use stm32f4xx_hal::gpio::gpioc::PC6; 21 | /// use stm32f4xx_hal::gpio::Alternate; 22 | /// 23 | /// type Monitor = PwmInput>>; 24 | /// 25 | /// fn tim8_cc2(monitor: &Monitor) { 26 | /// let duty_clocks = monitor.get_duty_cycle_clocks(); 27 | /// let period_clocks = monitor.get_period_clocks(); 28 | /// // check if this interrupt was caused by a capture at the wrong CC2, 29 | /// // peripheral limitation. 30 | /// if !monitor.is_valid_capture(){ 31 | /// return; 32 | /// } 33 | /// let duty = monitor.get_duty_cycle(); 34 | /// } 35 | /// ``` 36 | pub struct PwmInput 37 | where 38 | TIM: Instance + WithPwm, 39 | PINS: Pins, 40 | { 41 | timer: Timer, 42 | _pins: PINS, 43 | } 44 | 45 | impl Deref for PwmInput 46 | where 47 | TIM: Instance + WithPwm, 48 | PINS: Pins, 49 | { 50 | type Target = Timer; 51 | fn deref(&self) -> &Self::Target { 52 | &self.timer 53 | } 54 | } 55 | 56 | impl DerefMut for PwmInput 57 | where 58 | TIM: Instance + WithPwm, 59 | PINS: Pins, 60 | { 61 | fn deref_mut(&mut self) -> &mut Self::Target { 62 | &mut self.timer 63 | } 64 | } 65 | 66 | impl PwmInput 67 | where 68 | TIM: Instance + WithPwm, 69 | PINS: Pins, 70 | { 71 | pub fn release(mut self) -> Timer { 72 | self.tim.cr1_reset(); 73 | self.timer 74 | } 75 | } 76 | 77 | #[cfg(not(feature = "stm32f410"))] 78 | macro_rules! hal { 79 | ($($TIM:ident,)+) => { 80 | $( 81 | // Drag the associated TIM object into scope. 82 | // Note: its drawn in via the macro to avoid duplicating the feature gate this macro is 83 | // expecting to be guarded by. 84 | use crate::pac::$TIM; 85 | 86 | impl Timer<$TIM> { 87 | /// Configures this timer for PWM input. Accepts the `best_guess` frequency of the signal 88 | /// Note: this should be as close as possible to the frequency of the PWM waveform for best 89 | /// accuracy. 90 | /// 91 | /// This device will emit an interrupt on CC1, which occurs at two times in this mode: 92 | /// 1. When a new cycle is started: the duty cycle will be `1.00` 93 | /// 2. When the period is captured. the duty cycle will be an observable value. 94 | /// See the pwm input example for an suitable interrupt handler. 95 | #[allow(unused_unsafe)] //for some chips the operations are considered safe. 96 | pub fn pwm_input(mut self, best_guess: Hertz, pins: PINS) -> PwmInput<$TIM, PINS> 97 | where 98 | PINS: Pins<$TIM>, 99 | { 100 | /* 101 | Borrowed from PWM implementation. 102 | Sets the TIMer's prescaler such that the TIMer that it ticks at about the best-guess 103 | frequency. 104 | */ 105 | let ticks = self.clk.raw() / best_guess.raw(); 106 | let psc = u16::try_from((ticks - 1) / (1 << 16)).unwrap(); 107 | self.tim.set_prescaler(psc); 108 | 109 | // Seemingly this needs to be written to 110 | // self.tim.arr.write(|w| w.arr().bits(u16::MAX)); 111 | 112 | /* 113 | For example, one can measure the period (in TIMx_CCR1 register) and the duty cycle (in 114 | TIMx_CCR2 register) of the PWM applied on TI1 using the following procedure (depending 115 | on CK_INT frequency and prescaler value): 116 | 117 | from RM0390 16.3.7 118 | */ 119 | 120 | // Select the active input for TIMx_CCR1: write the CC1S bits to 01 in the TIMx_CCMR1 121 | // register (TI1 selected). 122 | self.tim 123 | .ccmr1_input() 124 | .modify(|_, w| unsafe { w.cc1s().bits(0b01) }); 125 | 126 | // Select the active polarity for TI1FP1 (used both for capture in TIMx_CCR1 and counter 127 | // clear): write the CC1P and CC1NP bits to ‘0’ (active on rising edge). 128 | 129 | self.tim 130 | .ccer 131 | .modify(|_, w| w.cc1p().clear_bit().cc2p().clear_bit()); 132 | 133 | // disable filters and disable the input capture prescalers. 134 | self.tim.ccmr1_input().modify(|_, w| unsafe { 135 | w.ic1f() 136 | .bits(0) 137 | .ic2f() 138 | .bits(0) 139 | .ic1psc() 140 | .bits(0) 141 | .ic2psc() 142 | .bits(0) 143 | }); 144 | 145 | // Select the active input for TIMx_CCR2: write the CC2S bits to 10 in the TIMx_CCMR1 146 | // register (TI1 selected) 147 | self.tim 148 | .ccmr1_input() 149 | .modify(|_, w| unsafe { w.cc2s().bits(0b10) }); 150 | 151 | // Select the active polarity for TI1FP2 (used for capture in TIMx_CCR2): write the CC2P 152 | // and CC2NP bits to ‘1’ (active on falling edge). 153 | self.tim 154 | .ccer 155 | .modify(|_, w| w.cc2p().set_bit().cc2np().set_bit()); 156 | 157 | // Select the valid trigger input: write the TS bits to 101 in the TIMx_SMCR register 158 | // (TI1FP1 selected). 159 | self.tim.smcr.modify(|_, w| unsafe { w.ts().bits(0b101) }); 160 | 161 | // Configure the slave mode controller in reset mode: write the SMS bits to 100 in the 162 | // TIMx_SMCR register. 163 | self.tim.smcr.modify(|_, w| unsafe { w.sms().bits(0b100) }); 164 | 165 | // Enable the captures: write the CC1E and CC2E bits to ‘1’ in the TIMx_CCER register. 166 | self.tim 167 | .ccer 168 | .modify(|_, w| w.cc1e().set_bit().cc2e().set_bit()); 169 | 170 | // enable interrupts. 171 | self.tim.dier.modify(|_, w| w.cc2ie().set_bit()); 172 | // enable the counter. 173 | self.tim.enable_counter(); 174 | 175 | PwmInput { timer: self, _pins: pins } 176 | } 177 | } 178 | 179 | impl PwmInput<$TIM, PINS> 180 | where 181 | PINS: Pins<$TIM>, 182 | { 183 | /// Period of PWM signal in terms of clock cycles 184 | pub fn get_period_clocks(&self) -> <$TIM as General>::Width { 185 | self.tim.ccr1().read().ccr().bits() 186 | } 187 | /// Duty cycle in terms of clock cycles 188 | pub fn get_duty_cycle_clocks(&self) -> <$TIM as General>::Width { 189 | self.tim.ccr2().read().ccr().bits() 190 | } 191 | /// Observed duty cycle as a float in range [0.00, 1.00] 192 | pub fn get_duty_cycle(&self) -> f32 { 193 | let period_clocks = self.get_period_clocks(); 194 | if period_clocks == 0 { 195 | return 0.0; 196 | }; 197 | return (self.get_duty_cycle_clocks() as f32 / period_clocks as f32) * 100f32; 198 | } 199 | /// Returns whether the timer's duty cycle is a valid observation 200 | /// (Limitation of how the captures work is extra CC2 interrupts are generated when the 201 | /// PWM cycle enters a new period). 202 | pub fn is_valid_capture(&self) -> bool { 203 | self.get_duty_cycle_clocks() != self.get_period_clocks() 204 | } 205 | } 206 | )+ 207 | }} 208 | 209 | #[cfg(any(feature = "stm32f411",))] 210 | /* red group */ 211 | hal! { 212 | TIM4, 213 | TIM3, 214 | TIM2, 215 | } 216 | 217 | /* orange group */ 218 | #[cfg(any( 219 | feature = "stm32f401", 220 | feature = "stm32f405", 221 | feature = "stm32f407", 222 | feature = "stm32f412", 223 | feature = "stm32f413", 224 | feature = "stm32f415", 225 | feature = "stm32f417", 226 | feature = "stm32f423", 227 | feature = "stm32f427", 228 | feature = "stm32f429", 229 | feature = "stm32f437", 230 | feature = "stm32f439", 231 | feature = "stm32f446", 232 | feature = "stm32f469", 233 | feature = "stm32f479", 234 | ))] 235 | hal! { 236 | TIM2, 237 | TIM3, 238 | TIM4, 239 | } 240 | /* green group */ 241 | #[cfg(any( 242 | feature = "stm32f405", 243 | feature = "stm32f407", 244 | feature = "stm32f412", 245 | feature = "stm32f413", 246 | feature = "stm32f415", 247 | feature = "stm32f417", 248 | feature = "stm32f423", 249 | feature = "stm32f427", 250 | feature = "stm32f429", 251 | feature = "stm32f437", 252 | feature = "stm32f439", 253 | feature = "stm32f446", 254 | feature = "stm32f469", 255 | feature = "stm32f479", 256 | ))] 257 | hal! { 258 | TIM8, 259 | TIM12, 260 | } 261 | 262 | /* every chip across the series have these timers with support for this feature. 263 | .. except for the 410 which, while the timers support this feature, has a different configuration 264 | than the rest of the series. 265 | */ 266 | /* yellow group */ 267 | #[cfg(not(feature = "stm32f410"))] 268 | hal! { 269 | TIM1, 270 | TIM5, 271 | TIM9, 272 | } 273 | -------------------------------------------------------------------------------- /src/watchdog.rs: -------------------------------------------------------------------------------- 1 | //! Watchdog peripherals 2 | 3 | use crate::pac::{DBGMCU, IWDG}; 4 | use core::fmt; 5 | use embedded_hal::watchdog::{Watchdog, WatchdogEnable}; 6 | use fugit::MillisDurationU32 as MilliSeconds; 7 | 8 | /// Wraps the Independent Watchdog (IWDG) peripheral 9 | pub struct IndependentWatchdog { 10 | iwdg: IWDG, 11 | } 12 | 13 | /* 14 | #[cfg(feature = "defmt")] 15 | impl defmt::Format for IndependentWatchdog { 16 | fn format(&self, f: defmt::Formatter) { 17 | defmt::write!(f, "IndependentWatchdog"); 18 | } 19 | } 20 | */ 21 | 22 | impl fmt::Debug for IndependentWatchdog { 23 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 24 | f.write_str("IndependentWatchdog") 25 | } 26 | } 27 | 28 | const MAX_PR: u8 = 0b110; 29 | const MAX_RL: u16 = 0xFFF; 30 | const KR_ACCESS: u16 = 0x5555; 31 | const KR_RELOAD: u16 = 0xAAAA; 32 | const KR_START: u16 = 0xCCCC; 33 | const LSI_KHZ: u32 = 32; 34 | 35 | impl IndependentWatchdog { 36 | /// Wrap and start the watchdog 37 | pub fn new(iwdg: IWDG) -> Self { 38 | IndependentWatchdog { iwdg } 39 | } 40 | 41 | /// Debug independent watchdog stopped when core is halted 42 | pub fn stop_on_debug(&self, dbgmcu: &DBGMCU, stop: bool) { 43 | dbgmcu.apb1_fz.modify(|_, w| w.dbg_iwdg_stop().bit(stop)); 44 | } 45 | 46 | fn setup(&self, timeout_ms: u32) { 47 | let mut pr = 0; 48 | while pr < MAX_PR && Self::timeout_period(pr, MAX_RL) < timeout_ms { 49 | pr += 1; 50 | } 51 | 52 | let max_period = Self::timeout_period(pr, MAX_RL); 53 | let max_rl = u32::from(MAX_RL); 54 | let rl = (timeout_ms * max_rl / max_period).min(max_rl) as u16; 55 | 56 | self.access_registers(|iwdg| { 57 | iwdg.pr.modify(|_, w| w.pr().bits(pr)); 58 | iwdg.rlr.modify(|_, w| w.rl().bits(rl)); 59 | }); 60 | } 61 | 62 | fn is_pr_updating(&self) -> bool { 63 | self.iwdg.sr.read().pvu().bit() 64 | } 65 | 66 | /// Returns the interval in ms 67 | pub fn interval(&self) -> MilliSeconds { 68 | while self.is_pr_updating() {} 69 | 70 | let pr = self.iwdg.pr.read().pr().bits(); 71 | let rl = self.iwdg.rlr.read().rl().bits(); 72 | let ms = Self::timeout_period(pr, rl); 73 | MilliSeconds::from_ticks(ms) 74 | } 75 | 76 | /// pr: Prescaler divider bits, rl: reload value 77 | /// 78 | /// Returns ms 79 | fn timeout_period(pr: u8, rl: u16) -> u32 { 80 | let divider: u32 = match pr { 81 | 0b000 => 4, 82 | 0b001 => 8, 83 | 0b010 => 16, 84 | 0b011 => 32, 85 | 0b100 => 64, 86 | 0b101 => 128, 87 | 0b110 => 256, 88 | 0b111 => 256, 89 | _ => unreachable!(), 90 | }; 91 | (u32::from(rl) + 1) * divider / LSI_KHZ 92 | } 93 | 94 | fn access_registers A>(&self, mut f: F) -> A { 95 | // Unprotect write access to registers 96 | self.iwdg.kr.write(|w| unsafe { w.key().bits(KR_ACCESS) }); 97 | let a = f(&self.iwdg); 98 | 99 | // Protect again 100 | self.iwdg.kr.write(|w| unsafe { w.key().bits(KR_RELOAD) }); 101 | a 102 | } 103 | 104 | pub fn start(&mut self, period: MilliSeconds) { 105 | self.setup(period.ticks()); 106 | 107 | self.iwdg.kr.write(|w| unsafe { w.key().bits(KR_START) }); 108 | } 109 | 110 | pub fn feed(&mut self) { 111 | self.iwdg.kr.write(|w| unsafe { w.key().bits(KR_RELOAD) }); 112 | } 113 | } 114 | 115 | impl WatchdogEnable for IndependentWatchdog { 116 | type Time = MilliSeconds; 117 | 118 | fn start>(&mut self, period: T) { 119 | self.start(period.into()) 120 | } 121 | } 122 | 123 | impl Watchdog for IndependentWatchdog { 124 | fn feed(&mut self) { 125 | self.feed() 126 | } 127 | } 128 | --------------------------------------------------------------------------------