├── .cargo └── config.toml ├── .github ├── bors.toml └── workflows │ ├── changelog.yml │ ├── ci.yml │ ├── clippy.yml │ └── rustfmt.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-0BSD.txt ├── README.md ├── examples ├── adc_values.rs ├── blinky.rs ├── blinky_adc.rs ├── blinky_delay.rs ├── blinky_multiple.rs ├── blinky_timer.rs ├── blinky_timer_irq.rs ├── dac.rs ├── defmt.rs ├── flash_read_write.rs ├── flash_systick.rs ├── flash_systick_fancier.rs ├── i2c_find_address.rs ├── led_hal_button_irq.rs ├── pwm.rs ├── pwm_complementary.rs ├── serial_echo.rs ├── serial_spi_bridge.rs ├── serial_stopwatch.rs ├── spi_hal_apa102c.rs ├── usb_serial.rs └── watchdog.rs ├── memory.x ├── openocd.cfg ├── openocd_program.sh ├── src ├── adc.rs ├── can.rs ├── dac.rs ├── delay.rs ├── flash.rs ├── gpio.rs ├── i2c.rs ├── lib.rs ├── prelude.rs ├── pwm.rs ├── rcc.rs ├── serial.rs ├── signature.rs ├── spi.rs ├── time.rs ├── timers.rs ├── tsc.rs ├── usb.rs └── watchdog.rs └── tools ├── capture_example_bloat.sh ├── capture_nightly_example_bloat.sh └── check.py /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.thumbv6m-none-eabi] 2 | runner = 'probe-rs run --protocol=swd --chip STM32F042K6' 3 | rustflags = [ 4 | "-C", "link-arg=-Tlink.x", 5 | "-C", "link-arg=--nmagic", 6 | "-C", "link-arg=-Tdefmt.x", 7 | ] 8 | 9 | [build] 10 | target = "thumbv6m-none-eabi" 11 | -------------------------------------------------------------------------------- /.github/bors.toml: -------------------------------------------------------------------------------- 1 | required_approvals = 1 2 | block_labels = ["wip"] 3 | delete_merged_branches = true 4 | status = [ 5 | "Rustfmt", 6 | "ci (stable)", 7 | ] 8 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request_target: 3 | 4 | name: Changelog check 5 | 6 | jobs: 7 | changelog: 8 | name: Changelog check 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout sources 12 | uses: actions/checkout@v2 13 | 14 | - name: Changelog updated 15 | uses: Zomzog/changelog-checker@v1.1.0 16 | with: 17 | fileName: CHANGELOG.md 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ staging, trying, master ] 4 | pull_request: 5 | 6 | name: Continuous integration 7 | 8 | jobs: 9 | ci: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | rust: 14 | - stable 15 | include: 16 | - rust: nightly 17 | experimental: true 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: actions-rs/toolchain@v1 22 | with: 23 | profile: minimal 24 | toolchain: ${{ matrix.rust }} 25 | target: thumbv6m-none-eabi 26 | override: true 27 | 28 | - name: Regular build 29 | run: python tools/check.py 30 | 31 | - name: Size check 32 | run: python tools/check.py size_check 33 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ staging, trying, master ] 4 | pull_request: 5 | 6 | name: Clippy check 7 | jobs: 8 | clippy_check: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - run: rustup component add clippy 13 | - uses: actions-rs/toolchain@v1 14 | with: 15 | toolchain: stable 16 | target: thumbv6m-none-eabi 17 | override: true 18 | - uses: actions-rs/clippy-check@v1 19 | with: 20 | token: ${{ secrets.GITHUB_TOKEN }} 21 | args: --features=stm32f072 --target thumbv6m-none-eabi 22 | -------------------------------------------------------------------------------- /.github/workflows/rustfmt.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ staging, trying, master ] 4 | pull_request: 5 | 6 | name: Code formatting check 7 | 8 | jobs: 9 | fmt: 10 | name: Rustfmt 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions-rs/toolchain@v1 15 | with: 16 | profile: minimal 17 | toolchain: stable 18 | override: true 19 | - run: rustup component add rustfmt 20 | - uses: actions-rs/cargo@v1 21 | with: 22 | command: fmt 23 | args: --all -- --check 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.orig 3 | **/*.rs.bk 4 | Cargo.lock 5 | *.txt 6 | -------------------------------------------------------------------------------- /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 | ## [Unreleased] 9 | 10 | ### Changed 11 | 12 | - Updated the `cast` dependency from 0.2 to 0.3 13 | - Updated `stm32f0` peripheral access crate from 0.14 to 0.15 14 | - Updated `bxcan` dependency from 0.6.0 to 0.8.0 15 | 16 | ### Added 17 | 18 | - Provide getters to serial status flags idle/txe/rxne/tc. 19 | - Provide ability to reset timer UIF interrupt flag 20 | - PWM complementary output capability for TIM1 with new example to demonstrate 21 | - Implement interface for reading and writing to the internal flash memory and an example for demonstration. 22 | - PWM output on complementary channels only for single channel timers (TIM16 + TIM17) 23 | - impl embedded_hal_1::spi::SpiBus for SPI 24 | - impl embedded_hal_1::digital traits for Pins 25 | 26 | ### Fixed 27 | 28 | - Wrong mode when using PWM channel 2 of a two-channel timer 29 | - `adc_values` example conversion error 30 | - `invalid_reference_casting` Compilation error in spi.rs for Rust version 1.73+ ( 31 | See [PR#112431](https://github.com/rust-lang/rust/pull/112431) for more info) 32 | - `unused_doc_comments` Warning in rcc.rs 33 | - Fixed some warnings #177 34 | 35 | ## [v0.18.0] - 2021-11-14 36 | 37 | ### Changed 38 | 39 | - Updated stm32f0 dependency to v0.13 (breaking change) 40 | - Move SPI busy polling to `check_send()` 41 | - Poll for SPI transaction completion before returning 42 | - Update `remap_pins()` and remove critical section 43 | - Updated `stm32f0` peripheral access crate from 0.13 to 0.14 44 | - Updated the `bxcan` dependency from 0.4.0 to 0.6.0 45 | - Address a few clippy lints 46 | - Fix `VTemp::convert_temp()` 47 | 48 | ### Added 49 | 50 | - Add CAN bus abstraction based on the [bxcan] crate. 51 | - Add PWM output generation based on the timers. 52 | - Added `impl From for Hertz` 53 | - Added `impl From for Hertz` 54 | - Added `impl From for MegaHertz` 55 | - Added `impl From for IwdgTimeout` 56 | 57 | [bxcan]: https://crates.io/crates/bxcan 58 | 59 | ## [v0.17.1] - 2020-08-30 60 | 61 | ### Changed 62 | 63 | - Simplify USB PA11/12 remapping for STM32F042x via `usb_bus.usb_remap()` function. 64 | 65 | ### Added 66 | 67 | - Complete the `TscPin` trait implementation for all touch pins in the f0 family 68 | 69 | ## [v0.17.0] - 2020-06-27 70 | 71 | ### Changed 72 | 73 | - Remove duplicate error bits clearing in serial `read()` implementation 74 | - Optimize SPI implementation 75 | - Use `pac` instead of `stm32` for PAC access and soft-deprecate the former 76 | - Updated stm32f0 dependency to v0.11 (breaking change) 77 | 78 | ### Added 79 | 80 | - Add 16bit SPI transfers 81 | - Another example resembling a stop watch controlled via serial interface 82 | 83 | ### Fixed 84 | 85 | - Incorrect PLLSRC bits when using HSE 86 | 87 | ## [v0.16.0] - 2020-02-02 88 | 89 | ### Added 90 | 91 | - Another blinky example using a timer interrupt 92 | 93 | ### Changed 94 | 95 | - Added "bypass" parameter to Rcc HSE configuration (breaking change) 96 | - Add "usbsrc" function to Rcc configuration, used for selecting USB clock source 97 | - For STM32F030, require use more specific feature flag, e.g. "stm32f030xc" 98 | - Add `embedded-hal` `blocking::i2c::Read` implementation for I2C 99 | - Added USB driver 100 | 101 | ### Fixed 102 | 103 | - Timer: Fix use of wrong frequency when HCLK != PCLK 104 | - RCC: Correct code to enable PLL 105 | - RCC: Correct calculation of PLL multiplier 106 | 107 | ## [v0.15.2] - 2019-11-04 108 | 109 | ### Changed 110 | 111 | - Re-enabled LTO 112 | - Changed digital pin functionality to implement v2 versions 113 | - Fixed a few deprecation warning and lints 114 | - Enabled commented out and now available GPIOE support for 07x and 09x families 115 | - Extract register block address only once 116 | - Add DAC driver 117 | 118 | ## [v0.15.1] - 2019-08-11 119 | 120 | ### Fixed 121 | 122 | - Clear UART errors in hardware after handling them 123 | 124 | ## [v0.15.0] - 2019-08-09 125 | 126 | ### Changed 127 | 128 | - Updated stm32f0 dependency to v0.8.0 (breaking change) 129 | - Made blinky example more universal by reducing CS 130 | 131 | ### Added 132 | 133 | - Added fancier example moving a resource into an exception handler 134 | 135 | ## [v0.14.1] - 2019-06-06 136 | 137 | ### Added 138 | 139 | - Support for CRS for devices with USB and HSI48 140 | 141 | ### Changed 142 | 143 | - Clear error flags in serial read() before returning 144 | - Revised feature flags for HSI48 clock support 145 | 146 | ## [v0.14.0] - 2019-04-25 147 | 148 | ### Changed 149 | 150 | - Updated stm32f0 dependency to v0.7.0 (breaking change) - @jessebraham 151 | - Bumped cortex-m dependency to ">=0.5.8,<0.7.0" to let user decide version 152 | - Bumped cortex-m-rt dependency to v0.6.8 153 | 154 | ## [v0.13.0] - 2019-02-06 155 | 156 | ### Added 157 | 158 | - Support for stm32f0x8 line - @jessebraham 159 | - Support for capacitive touch sensing (TSC) 160 | 161 | ### Changed 162 | 163 | - Updated to stm32-rs v0.6.0 - @HarkonenBade 164 | - Updated the ADC code to use variants added in stm32-rs v0.6.0 - @HarkonenBade 165 | - Improved serial `write_str` implementation 166 | 167 | ### Fixed 168 | 169 | - Fixed ADC use trampling over the HSI48 clock settings 170 | 171 | ## [v0.12.0] - 2019-01-13 172 | 173 | ### Added 174 | 175 | - Support for stm32f0x1 line - @jessebraham 176 | - Support for HSE as a system clocksource (#25 - breaking change) - @zklapow 177 | - Add ability to use a Tx/Rx only serial instance - @david-sawatzke 178 | 179 | ### Changed 180 | 181 | - Optimize delay implemenation (#42) - @david-sawatzke 182 | - Enforced more rigorous safety guarentees (#41 - Very breaking change) - @HarkonenBade 183 | 184 | ### Fixed 185 | 186 | - Fixed panic in delay overflow handling for debug builds - @david-sawatzke 187 | 188 | ## [v0.11.1] - 2019-01-05 189 | 190 | ### Added 191 | 192 | - Added peripheral definitions for the stm32f072xx line - @Yatekii 193 | 194 | ### Changed 195 | 196 | - Fixed broken PC GPIO definitions with feature = "stm32f030" and feature = "stm32f070" 197 | - More robust error handling for I2C 198 | 199 | ## [v0.11.0] - 2019-01-04 200 | 201 | ### Added 202 | 203 | - Added ADC helper functions to read more intuitive values (#22) - @HarkonenBade 204 | - Added interrupt enabling/disabling support to USART ports 205 | - Added the option to have multiple Delay instances by cloning it - @david-sawatzke 206 | 207 | ### Changed 208 | 209 | - Fixed a few clippy lints 210 | 211 | ### Removed 212 | 213 | - Removed the free() method on the Delay provider (breaking change) 214 | 215 | ## [v0.10.1] - 2018-12-25 216 | 217 | ### Added 218 | 219 | - Added Sync & Send ability to Pin 220 | - Added initial implementation of an ADC interface (#13) - @HarkonenBade 221 | - Added virtual-feature "device-selected" to simplify feature gating 222 | 223 | ### Changed 224 | 225 | - Added overflow guards to delay 226 | 227 | ## [v0.10.0] - 2018-12-23 228 | 229 | ### Added 230 | 231 | - Reworked GPIOs and added fully erased pins 232 | - Timer support 233 | - Support for STM32F070 - @jessebraham 234 | - Additional peripheral support for STM32F030 235 | - Watchdog support 236 | 237 | ### Changed 238 | 239 | - Removed superfluous use statements 240 | - Re-added Send ability for U(S)ART Rx/Tx 241 | - Made crate to compile without features 242 | - Eliminated a lot of unused warnings 243 | 244 | ### Fixed 245 | 246 | - Fixed some comments 247 | - Changed some prelude aliases to reflect crate name 248 | 249 | ### Removed 250 | 251 | - Examples requiring additional driver crates 252 | 253 | ## [v0.9.0] - 2018-12-17 254 | 255 | ### Added 256 | 257 | - Toggleable implementation for GPIOs 258 | - Initial support for STM32F030 259 | - LICENSE file 260 | 261 | ### Changed 262 | 263 | - Updated stm32f0 dependency to v0.5.0. 264 | - Interrupt handler to new #[interrupt] attribute 265 | 266 | [Unreleased]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.18.0...HEAD 267 | [v0.18.0]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.17.1...v0.18.0 268 | [v0.17.1]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.17.0...v0.17.1 269 | [v0.17.0]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.16.0...v0.17.0 270 | [v0.16.0]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.15.2...v0.16.0 271 | [v0.15.2]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.15.1...v0.15.2 272 | [v0.15.1]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.15.0...v0.15.1 273 | [v0.15.0]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.14.1...v0.15.0 274 | [v0.14.1]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.14.0...v0.14.1 275 | [v0.14.0]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.13.0...v0.14.0 276 | [v0.13.0]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.12.0...v0.13.0 277 | [v0.12.0]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.11.1...v0.12.0 278 | [v0.11.1]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.11.0...v0.11.1 279 | [v0.11.0]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.10.1...v0.11.0 280 | [v0.10.1]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.10.0...v0.10.1 281 | [v0.10.0]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.9.0...v0.10.0 282 | [v0.9.0]: https://github.com/stm32-rs/stm32f0xx-hal/compare/v0.8.0...v0.9.0 283 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2018" 3 | authors = [ 4 | "Daniel Egger ", 5 | "Thomas Bytheway ", 6 | "Jesse Braham ", 7 | ] 8 | categories = [ 9 | "embedded", 10 | "hardware-support", 11 | "no-std", 12 | ] 13 | description = "Peripheral access API for STM32F0 series microcontrollers" 14 | documentation = "https://docs.rs/crate/stm32f0xx-hal" 15 | 16 | keywords = [ 17 | "arm", 18 | "cortex-m", 19 | "stm32f0xx", 20 | "hal", 21 | ] 22 | license = "0BSD" 23 | name = "stm32f0xx-hal" 24 | readme = "README.md" 25 | repository = "https://github.com/stm32-rs/stm32f0xx-hal" 26 | version = "0.18.0" 27 | 28 | [package.metadata.docs.rs] 29 | features = ["stm32f042", "rt", "stm32-usbd"] 30 | targets = ["thumbv6m-none-eabi"] 31 | 32 | [profile.dev.package."*"] 33 | opt-level = 's' 34 | 35 | [dependencies] 36 | bare-metal = { version = "1.0.0" } 37 | cast = "0.3" 38 | cortex-m = { version = "0.7", features = ["critical-section-single-core"]} 39 | embedded-hal = { version = "0.2", features = ["unproven"] } 40 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 41 | stm32f0 = "0.15" 42 | nb = "1" 43 | void = { version = "1.0", default-features = false } 44 | stm32-usbd = { version = "0.6", optional = true } 45 | bxcan = "0.8.0" 46 | embedded-storage = "0.3.0" 47 | 48 | [dev-dependencies] 49 | cortex-m-rt = "0.7" 50 | panic-halt = "1" 51 | usb-device = "0.2.7" 52 | usbd-serial = "0.1.1" 53 | defmt = "1.0.1" 54 | defmt-rtt = "1.0.0" 55 | 56 | [features] 57 | device-selected = [] 58 | rt = ["stm32f0/rt"] 59 | stm32f030 = ["stm32f0/stm32f0x0", "device-selected"] 60 | stm32f030x4 = ["stm32f030"] 61 | stm32f030x6 = ["stm32f030"] 62 | stm32f030x8 = ["stm32f030"] 63 | stm32f030xc = ["stm32f030"] 64 | stm32f031 = ["stm32f0/stm32f0x1", "device-selected"] 65 | stm32f038 = ["stm32f0/stm32f0x8", "device-selected"] 66 | stm32f042 = ["stm32f0/stm32f0x2", "device-selected"] 67 | stm32f048 = ["stm32f0/stm32f0x8", "device-selected"] 68 | stm32f051 = ["stm32f0/stm32f0x1", "device-selected"] 69 | stm32f058 = ["stm32f0/stm32f0x8", "device-selected"] 70 | stm32f070 = ["stm32f0/stm32f0x0", "device-selected"] 71 | stm32f070x6 = ["stm32f070"] 72 | stm32f070xb = ["stm32f070"] 73 | stm32f071 = ["stm32f0/stm32f0x1", "device-selected"] 74 | stm32f072 = ["stm32f0/stm32f0x2", "device-selected"] 75 | stm32f078 = ["stm32f0/stm32f0x8", "device-selected"] 76 | stm32f091 = ["stm32f0/stm32f0x1", "device-selected"] 77 | stm32f098 = ["stm32f0/stm32f0x8", "device-selected"] 78 | 79 | [profile.dev] 80 | debug = true 81 | lto = true 82 | 83 | [profile.release] 84 | lto = true 85 | debug = true 86 | opt-level = "s" 87 | 88 | [[example]] 89 | name = "blinky_timer_irq" 90 | required-features = ["stm32f072", "rt"] 91 | 92 | [[example]] 93 | name = "serial_stopwatch" 94 | required-features = ["stm32f072", "rt"] 95 | 96 | [[example]] 97 | name = "dac" 98 | required-features = ["stm32f072"] 99 | 100 | [[example]] 101 | name = "led_hal_button_irq" 102 | required-features = ["stm32f042", "rt"] 103 | 104 | [[example]] 105 | name = "usb_serial" 106 | required-features = ["rt", "stm32f042", "stm32-usbd"] 107 | -------------------------------------------------------------------------------- /LICENSE-0BSD.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2018 daniel@eggers-club.de 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 7 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 8 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 9 | SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 10 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 11 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 12 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | stm32f0xx-hal 2 | ============= 3 | 4 | [![Continuous integration](https://github.com/stm32-rs/stm32f0xx-hal/workflows/Continuous%20integration/badge.svg)](https://github.com/stm32-rs/stm32f0xx-hal) 5 | [![Crates.io](https://img.shields.io/crates/v/stm32f0xx-hal.svg)](https://crates.io/crates/stm32f0xx-hal) 6 | [![docs.rs](https://docs.rs/stm32f0xx-hal/badge.svg)](https://docs.rs/stm32f0xx-hal/) 7 | 8 | [_stm32f0xx-hal_](https://github.com/stm32-rs/stm32f0xx-hal) contains a hardware abstraction on top of the peripheral access API for the STMicro STM32F0xx family of microcontrollers. 9 | 10 | This crate replaces the [stm32f042-hal](https://github.com/therealprof/stm32f042-hal) by a more ubiquitous version suitable for additional families. The idea behind this crate is to gloss over the slight differences in the various peripherals available on those MCUs so a HAL can be written for all chips in that same family without having to cut and paste crates for every single model. 11 | 12 | This crate relies on Adam Greig's fantastic [stm32f0](https://crates.io/crates/stm32f0) crate to provide appropriate register definitions, and implements a partial set of the [embedded-hal](https://github.com/rust-embedded/embedded-hal) traits. Some of the implementation was shamelessly adapted from the [stm32f103xx-hal](https://github.com/japaric/stm32f103xx-hal) crate by Jorge Aparicio. 13 | 14 | Collaboration on this crate is highly welcome, as are pull requests! 15 | 16 | 17 | Supported Configurations 18 | ------------------------ 19 | 20 | * __stm32f030__ (stm32f030x4, stm32f030x6, stm32f030x8, stm32f030xc) 21 | * __stm32f031__ 22 | * __stm32f038__ 23 | * __stm32f042__ 24 | * __stm32f048__ 25 | * __stm32f051__ 26 | * __stm32f058__ 27 | * __stm32f070__ (stm32f070x6, stm32f070xb) 28 | * __stm32f071__ 29 | * __stm32f072__ 30 | * __stm32f078__ 31 | * __stm32f091__ 32 | * __stm32f098__ 33 | 34 | 35 | Getting Started 36 | --------------- 37 | The `examples` folder contains several example programs. To compile them, one must specify the target device as cargo feature: 38 | ``` 39 | $ cargo build --features=stm32f030x4 40 | ``` 41 | 42 | To use stm32f0xx-hal as a dependency in a standalone project the target device feature must be specified in the `Cargo.toml` file: 43 | ``` 44 | [dependencies] 45 | cortex-m = "0.7" 46 | cortex-m-rt = "0.7" 47 | stm32f0xx-hal = { version = "0.18", features = ["stm32f030"]} 48 | ``` 49 | 50 | If you are unfamiliar with embedded development using Rust, there are a number of fantastic resources available to help. 51 | 52 | - [Embedded Rust Documentation](https://docs.rust-embedded.org/) 53 | - [The Embedded Rust Book](https://docs.rust-embedded.org/book/) 54 | - [Rust Embedded FAQ](https://docs.rust-embedded.org/faq.html) 55 | - [rust-embedded/awesome-embedded-rust](https://github.com/rust-embedded/awesome-embedded-rust) 56 | 57 | 58 | Minimum supported Rust version 59 | ------------------------------ 60 | 61 | The minimum supported Rust version is the latest stable release. Older versions may compile, especially when some features are not used in your application. 62 | 63 | Changelog 64 | --------- 65 | 66 | See [CHANGELOG.md](CHANGELOG.md). 67 | 68 | 69 | License 70 | ------- 71 | 72 | 0-Clause BSD License, see [LICENSE-0BSD.txt](LICENSE-0BSD.txt) for more details. 73 | -------------------------------------------------------------------------------- /examples/adc_values.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{pac, prelude::*}; 9 | 10 | use cortex_m::{interrupt::Mutex, peripheral::syst::SystClkSource::Core}; 11 | use cortex_m_rt::{entry, exception}; 12 | 13 | use core::{cell::RefCell, fmt::Write}; 14 | 15 | struct Shared { 16 | adc: hal::adc::Adc, 17 | tx: hal::serial::Tx, 18 | } 19 | 20 | static SHARED: Mutex>> = Mutex::new(RefCell::new(None)); 21 | 22 | #[entry] 23 | fn main() -> ! { 24 | if let (Some(p), Some(cp)) = ( 25 | hal::pac::Peripherals::take(), 26 | cortex_m::peripheral::Peripherals::take(), 27 | ) { 28 | cortex_m::interrupt::free(move |cs| { 29 | let mut flash = p.FLASH; 30 | let mut rcc = p.RCC.configure().sysclk(8.mhz()).freeze(&mut flash); 31 | 32 | let gpioa = p.GPIOA.split(&mut rcc); 33 | 34 | let mut syst = cp.SYST; 35 | 36 | // Set source for SysTick counter, here full operating frequency (== 8MHz) 37 | syst.set_clock_source(Core); 38 | 39 | // Set reload value, i.e. timer delay 8 MHz/counts 40 | syst.set_reload(8_000_000 - 1); 41 | 42 | // Start SysTick counter 43 | syst.enable_counter(); 44 | 45 | // Start SysTick interrupt generation 46 | syst.enable_interrupt(); 47 | 48 | // USART1 at PA9 (TX) and PA10(RX) 49 | let tx = gpioa.pa9.into_alternate_af1(cs); 50 | let rx = gpioa.pa10.into_alternate_af1(cs); 51 | 52 | // Initialiase UART 53 | let (mut tx, _) = 54 | hal::serial::Serial::usart1(p.USART1, (tx, rx), 115_200.bps(), &mut rcc).split(); 55 | 56 | // Initialise ADC 57 | let adc = hal::adc::Adc::new(p.ADC, &mut rcc); 58 | 59 | // Output a friendly greeting 60 | tx.write_str("\n\rThis ADC example will read various values using the ADC and print them out to the serial terminal\r\n").ok(); 61 | 62 | // Move all components under Mutex supervision 63 | *SHARED.borrow(cs).borrow_mut() = Some(Shared { adc, tx }); 64 | }); 65 | } 66 | 67 | loop { 68 | continue; 69 | } 70 | } 71 | 72 | #[exception] 73 | fn SysTick() { 74 | use core::ops::DerefMut; 75 | 76 | // Enter critical section 77 | cortex_m::interrupt::free(|cs| { 78 | // Get access to the Mutex protected shared data 79 | if let Some(ref mut shared) = SHARED.borrow(cs).borrow_mut().deref_mut() { 80 | // Read temperature data from internal sensor using ADC 81 | let t = hal::adc::VTemp::read(&mut shared.adc, None); 82 | writeln!(shared.tx, "Temperature {}.{}C\r", t / 10, t % 10).ok(); 83 | 84 | // Read volatage reference data from internal sensor using ADC 85 | let t = hal::adc::VRef::read_vdda(&mut shared.adc); 86 | writeln!(shared.tx, "Vdda {}mV\r", t).ok(); 87 | } 88 | }); 89 | } 90 | -------------------------------------------------------------------------------- /examples/blinky.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{pac, prelude::*}; 9 | 10 | use cortex_m_rt::entry; 11 | 12 | #[entry] 13 | fn main() -> ! { 14 | if let Some(mut p) = pac::Peripherals::take() { 15 | let mut rcc = p.RCC.configure().sysclk(8.mhz()).freeze(&mut p.FLASH); 16 | 17 | let gpioa = p.GPIOA.split(&mut rcc); 18 | 19 | // (Re-)configure PA1 as output 20 | let mut led = cortex_m::interrupt::free(|cs| gpioa.pa1.into_push_pull_output(cs)); 21 | 22 | loop { 23 | // Turn PA1 on a million times in a row 24 | for _ in 0..1_000_000 { 25 | led.set_high().ok(); 26 | } 27 | // Then turn PA1 off a million times in a row 28 | for _ in 0..1_000_000 { 29 | led.set_low().ok(); 30 | } 31 | } 32 | } 33 | 34 | loop { 35 | continue; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/blinky_adc.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{adc::Adc, delay::Delay, pac, prelude::*}; 9 | 10 | use cortex_m::peripheral::Peripherals; 11 | use cortex_m_rt::entry; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | if let (Some(mut p), Some(cp)) = (pac::Peripherals::take(), Peripherals::take()) { 16 | let mut rcc = p.RCC.configure().sysclk(8.mhz()).freeze(&mut p.FLASH); 17 | 18 | let gpioa = p.GPIOA.split(&mut rcc); 19 | 20 | let (mut led, mut an_in) = cortex_m::interrupt::free(move |cs| { 21 | ( 22 | // (Re-)configure PA1 as output 23 | gpioa.pa1.into_push_pull_output(cs), 24 | // (Re-)configure PA0 as analog input 25 | gpioa.pa0.into_analog(cs), 26 | ) 27 | }); 28 | 29 | // Get delay provider 30 | let mut delay = Delay::new(cp.SYST, &rcc); 31 | 32 | // Get access to the ADC 33 | let mut adc = Adc::new(p.ADC, &mut rcc); 34 | 35 | loop { 36 | led.toggle().ok(); 37 | 38 | let time: u16 = if let Ok(val) = adc.read(&mut an_in) as Result { 39 | /* shift the value right by 3, same as divide by 8, reduces 40 | the 0-4095 range into something approximating 1-512 */ 41 | (val >> 3) + 1 42 | } else { 43 | 1000 44 | }; 45 | 46 | delay.delay_ms(time); 47 | } 48 | } 49 | 50 | loop { 51 | continue; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/blinky_delay.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{delay::Delay, pac, prelude::*}; 9 | 10 | use cortex_m::peripheral::Peripherals; 11 | use cortex_m_rt::entry; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | if let (Some(mut p), Some(cp)) = (pac::Peripherals::take(), Peripherals::take()) { 16 | let mut rcc = p.RCC.configure().sysclk(8.mhz()).freeze(&mut p.FLASH); 17 | 18 | let gpioa = p.GPIOA.split(&mut rcc); 19 | 20 | // (Re-)configure PA1 as output 21 | let mut led = cortex_m::interrupt::free(move |cs| gpioa.pa1.into_push_pull_output(cs)); 22 | 23 | // Get delay provider 24 | let mut delay = Delay::new(cp.SYST, &rcc); 25 | 26 | loop { 27 | led.toggle().ok(); 28 | delay.delay_ms(1_000_u16); 29 | } 30 | } 31 | 32 | loop { 33 | continue; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/blinky_multiple.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{delay::Delay, pac, prelude::*}; 9 | 10 | use cortex_m::peripheral::Peripherals; 11 | use cortex_m_rt::entry; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | if let (Some(mut p), Some(cp)) = (pac::Peripherals::take(), Peripherals::take()) { 16 | let mut rcc = p.RCC.configure().sysclk(8.mhz()).freeze(&mut p.FLASH); 17 | 18 | let gpioa = p.GPIOA.split(&mut rcc); 19 | let gpiob = p.GPIOB.split(&mut rcc); 20 | 21 | let (led1, led2) = cortex_m::interrupt::free(move |cs| { 22 | ( 23 | // (Re-)configure PA1 as output 24 | gpioa.pa1.into_push_pull_output(cs), 25 | // (Re-)configure PB1 as output 26 | gpiob.pb1.into_push_pull_output(cs), 27 | ) 28 | }); 29 | 30 | // Get delay provider 31 | let mut delay = Delay::new(cp.SYST, &rcc); 32 | 33 | // Store them together after erasing the type 34 | let mut leds = [led1.downgrade(), led2.downgrade()]; 35 | loop { 36 | for l in &mut leds { 37 | l.set_high().ok(); 38 | } 39 | delay.delay_ms(1_000_u16); 40 | 41 | for l in &mut leds { 42 | l.set_low().ok(); 43 | } 44 | delay.delay_ms(1_000_u16); 45 | } 46 | } 47 | 48 | loop { 49 | continue; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/blinky_timer.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{pac, prelude::*, time::Hertz, timers::*}; 9 | 10 | use cortex_m_rt::entry; 11 | 12 | #[entry] 13 | fn main() -> ! { 14 | if let Some(mut p) = pac::Peripherals::take() { 15 | let mut rcc = p.RCC.configure().sysclk(8.mhz()).freeze(&mut p.FLASH); 16 | 17 | let gpioa = p.GPIOA.split(&mut rcc); 18 | 19 | // (Re-)configure PA1 as output 20 | let mut led = cortex_m::interrupt::free(move |cs| gpioa.pa1.into_push_pull_output(cs)); 21 | 22 | // Set up a timer expiring after 1s 23 | let mut timer = Timer::tim1(p.TIM1, Hertz(1), &mut rcc); 24 | 25 | loop { 26 | led.toggle().ok(); 27 | 28 | // Wait for the timer to expire 29 | nb::block!(timer.wait()).ok(); 30 | } 31 | } 32 | 33 | loop { 34 | continue; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/blinky_timer_irq.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{ 9 | gpio::*, 10 | pac::{interrupt, Interrupt, Peripherals, TIM7}, 11 | prelude::*, 12 | time::Hertz, 13 | timers::*, 14 | }; 15 | 16 | use cortex_m_rt::entry; 17 | 18 | use core::cell::RefCell; 19 | use cortex_m::{interrupt::Mutex, peripheral::Peripherals as c_m_Peripherals}; 20 | 21 | // A type definition for the GPIO pin to be used for our LED 22 | type LEDPIN = gpioa::PA5>; 23 | 24 | // Make LED pin globally available 25 | static GLED: Mutex>> = Mutex::new(RefCell::new(None)); 26 | 27 | // Make timer interrupt registers globally available 28 | static GINT: Mutex>>> = Mutex::new(RefCell::new(None)); 29 | 30 | // Define an interupt handler, i.e. function to call when interrupt occurs. Here if our external 31 | // interrupt trips when the timer timed out 32 | #[interrupt] 33 | fn TIM7() { 34 | static mut LED: Option = None; 35 | static mut INT: Option> = None; 36 | 37 | let led = LED.get_or_insert_with(|| { 38 | cortex_m::interrupt::free(|cs| { 39 | // Move LED pin here, leaving a None in its place 40 | GLED.borrow(cs).replace(None).unwrap() 41 | }) 42 | }); 43 | 44 | let int = INT.get_or_insert_with(|| { 45 | cortex_m::interrupt::free(|cs| { 46 | // Move LED pin here, leaving a None in its place 47 | GINT.borrow(cs).replace(None).unwrap() 48 | }) 49 | }); 50 | 51 | led.toggle().ok(); 52 | int.wait().ok(); 53 | } 54 | 55 | #[entry] 56 | fn main() -> ! { 57 | if let (Some(mut p), Some(cp)) = (Peripherals::take(), c_m_Peripherals::take()) { 58 | cortex_m::interrupt::free(move |cs| { 59 | let mut rcc = p 60 | .RCC 61 | .configure() 62 | .hsi48() 63 | .enable_crs(p.CRS) 64 | .sysclk(48.mhz()) 65 | .pclk(24.mhz()) 66 | .freeze(&mut p.FLASH); 67 | 68 | let gpioa = p.GPIOA.split(&mut rcc); 69 | 70 | // (Re-)configure PA5 as output 71 | let led = gpioa.pa5.into_push_pull_output(cs); 72 | 73 | // Move the pin into our global storage 74 | *GLED.borrow(cs).borrow_mut() = Some(led); 75 | 76 | // Set up a timer expiring after 1s 77 | let mut timer = Timer::tim7(p.TIM7, Hertz(1), &mut rcc); 78 | 79 | // Generate an interrupt when the timer expires 80 | timer.listen(Event::TimeOut); 81 | 82 | // Move the timer into our global storage 83 | *GINT.borrow(cs).borrow_mut() = Some(timer); 84 | 85 | // Enable TIM7 IRQ, set prio 1 and clear any pending IRQs 86 | let mut nvic = cp.NVIC; 87 | unsafe { 88 | nvic.set_priority(Interrupt::TIM7, 1); 89 | cortex_m::peripheral::NVIC::unmask(Interrupt::TIM7); 90 | } 91 | cortex_m::peripheral::NVIC::unpend(Interrupt::TIM7); 92 | }); 93 | } 94 | 95 | loop { 96 | continue; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /examples/dac.rs: -------------------------------------------------------------------------------- 1 | #![deny(unused_imports)] 2 | #![no_main] 3 | #![no_std] 4 | 5 | use cortex_m_rt as rt; 6 | use panic_halt as _; 7 | 8 | use stm32f0xx_hal as hal; 9 | 10 | use crate::hal::dac::*; 11 | use crate::hal::pac; 12 | use crate::hal::prelude::*; 13 | 14 | use rt::entry; 15 | 16 | enum Direction { 17 | Upcounting, 18 | Downcounting, 19 | } 20 | 21 | #[entry] 22 | fn main() -> ! { 23 | if let (Some(mut dp), Some(_cp)) = (pac::Peripherals::take(), cortex_m::Peripherals::take()) { 24 | let mut rcc = dp.RCC.configure().sysclk(8.mhz()).freeze(&mut dp.FLASH); 25 | let gpioa = dp.GPIOA.split(&mut rcc); 26 | 27 | let pa4 = cortex_m::interrupt::free(move |cs| gpioa.pa4.into_analog(cs)); 28 | 29 | let mut dac = dac(dp.DAC, pa4, &mut rcc); 30 | 31 | dac.enable(); 32 | 33 | let mut dir = Direction::Upcounting; 34 | let mut val = 0; 35 | 36 | dac.set_value(2058); 37 | cortex_m::asm::bkpt(); 38 | 39 | dac.set_value(4095); 40 | cortex_m::asm::bkpt(); 41 | 42 | loop { 43 | dac.set_value(val); 44 | match val { 45 | 0 => dir = Direction::Upcounting, 46 | 4095 => dir = Direction::Downcounting, 47 | _ => (), 48 | }; 49 | 50 | match dir { 51 | Direction::Upcounting => val += 1, 52 | Direction::Downcounting => val -= 1, 53 | } 54 | } 55 | } 56 | 57 | loop { 58 | continue; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/defmt.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use defmt_rtt as _; 5 | use panic_halt as _; 6 | 7 | use stm32f0xx_hal as _; 8 | 9 | use cortex_m_rt::entry; 10 | 11 | #[entry] 12 | fn main() -> ! { 13 | defmt::println!("Hello, world!"); 14 | 15 | loop {} 16 | } 17 | -------------------------------------------------------------------------------- /examples/flash_read_write.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | use stm32f0xx_hal as hal; 6 | 7 | use crate::hal::{ 8 | flash::{FlashExt, LockedFlash}, 9 | pac, 10 | prelude::*, 11 | }; 12 | 13 | use cortex_m_rt::entry; 14 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; 15 | 16 | /// # NOTE 17 | /// This example assumes a flash size of more than 16K. If your MCU has less or equal than 16K Bytes 18 | /// of flash memory, adjust the `memory.x` file and `OFFSET_START` + `OFFSET_END` constants accordingly. 19 | #[entry] 20 | fn main() -> ! { 21 | if let Some(mut p) = pac::Peripherals::take() { 22 | let _ = p.RCC.configure().freeze(&mut p.FLASH); 23 | 24 | // Check that flash is big enough for this example 25 | assert!(p.FLASH.len() > 16 * 1024); 26 | 27 | // All examples use the first 16K of flash for the program so we use the first page after that 28 | const OFFSET_START: u32 = 16 * 1024; 29 | const OFFSET_END: u32 = OFFSET_START + 1024; 30 | // Unlock flash before writing 31 | let mut unlocked_flash = p.FLASH.unlocked(); 32 | 33 | NorFlash::erase(&mut unlocked_flash, OFFSET_START, OFFSET_END).unwrap(); 34 | 35 | // Write some data to the start of that page 36 | let write_data = [0xC0_u8, 0xFF_u8, 0xEE_u8, 0x00_u8]; 37 | match NorFlash::write(&mut unlocked_flash, OFFSET_START, &write_data) { 38 | Err(_) => panic!(), 39 | Ok(_) => (), 40 | } 41 | 42 | // Read back the written data from flash 43 | let mut read_buffer: [u8; 4] = [0; 4]; 44 | unlocked_flash.read(OFFSET_START, &mut read_buffer).unwrap(); 45 | assert_eq!(write_data, read_buffer); 46 | 47 | // Lock flash by dropping it 48 | drop(unlocked_flash); 49 | 50 | // It is also possible to read "manually" using core functions 51 | let read_data = unsafe { 52 | core::slice::from_raw_parts( 53 | (p.FLASH.address() + OFFSET_START as usize) as *const u8, 54 | write_data.len(), 55 | ) 56 | }; 57 | for (i, d) in read_data.iter().enumerate() { 58 | read_buffer[i] = *d; 59 | } 60 | 61 | assert_eq!(write_data, read_buffer); 62 | 63 | // Reading is also possible on locked flash 64 | let mut locked_flash = LockedFlash::new(p.FLASH); 65 | locked_flash.read(OFFSET_START, &mut read_buffer).unwrap(); 66 | 67 | assert_eq!(write_data, read_buffer); 68 | } 69 | loop { 70 | continue; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /examples/flash_systick.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{gpio::*, pac, prelude::*}; 9 | 10 | use cortex_m::{interrupt::Mutex, peripheral::syst::SystClkSource::Core, Peripherals}; 11 | use cortex_m_rt::{entry, exception}; 12 | 13 | use core::cell::RefCell; 14 | use core::ops::DerefMut; 15 | 16 | // Mutex protected structure for our shared GPIO pin 17 | static GPIO: Mutex>>>> = Mutex::new(RefCell::new(None)); 18 | 19 | #[entry] 20 | fn main() -> ! { 21 | if let (Some(mut p), Some(cp)) = (pac::Peripherals::take(), Peripherals::take()) { 22 | cortex_m::interrupt::free(move |cs| { 23 | let mut rcc = p.RCC.configure().sysclk(48.mhz()).freeze(&mut p.FLASH); 24 | 25 | let gpioa = p.GPIOA.split(&mut rcc); 26 | 27 | // (Re-)configure PA1 as output 28 | let led = gpioa.pa1.into_push_pull_output(cs); 29 | 30 | // Transfer GPIO into a shared structure 31 | *GPIO.borrow(cs).borrow_mut() = Some(led); 32 | 33 | let mut syst = cp.SYST; 34 | 35 | // Initialise SysTick counter with a defined value 36 | unsafe { syst.cvr.write(1) }; 37 | 38 | // Set source for SysTick counter, here full operating frequency (== 48MHz) 39 | syst.set_clock_source(Core); 40 | 41 | // Set reload value, i.e. timer delay 48 MHz/4 Mcounts == 12Hz or 83ms 42 | syst.set_reload(4_000_000 - 1); 43 | 44 | // Start counting 45 | syst.enable_counter(); 46 | 47 | // Enable interrupt generation 48 | syst.enable_interrupt(); 49 | }); 50 | } 51 | 52 | loop { 53 | continue; 54 | } 55 | } 56 | 57 | // Define an exception handler, i.e. function to call when exception occurs. Here, if our SysTick 58 | // timer generates an exception the following handler will be called 59 | #[exception] 60 | fn SysTick() { 61 | // Exception handler state variable 62 | static mut STATE: u8 = 1; 63 | 64 | // Enter critical section 65 | cortex_m::interrupt::free(|cs| { 66 | // Borrow access to our GPIO pin from the shared structure 67 | if let Some(ref mut led) = *GPIO.borrow(cs).borrow_mut().deref_mut() { 68 | // Check state variable, keep LED off most of the time and turn it on every 10th tick 69 | if *STATE < 10 { 70 | // Turn off the LED 71 | led.set_low().ok(); 72 | 73 | // And now increment state variable 74 | *STATE += 1; 75 | } else { 76 | // Turn on the LED 77 | led.set_high().ok(); 78 | 79 | // And set new state variable back to 0 80 | *STATE = 0; 81 | } 82 | } 83 | }); 84 | } 85 | -------------------------------------------------------------------------------- /examples/flash_systick_fancier.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{gpio::*, pac, prelude::*}; 9 | 10 | use cortex_m::{interrupt::Mutex, peripheral::syst::SystClkSource::Core, Peripherals}; 11 | use cortex_m_rt::{entry, exception}; 12 | 13 | use core::cell::RefCell; 14 | use core::mem::swap; 15 | 16 | // A type definition for the GPIO pin to be used for our LED 17 | type LEDPIN = gpiob::PB3>; 18 | 19 | // Mutex protected structure for our shared GPIO pin 20 | static GPIO: Mutex>> = Mutex::new(RefCell::new(None)); 21 | 22 | #[entry] 23 | fn main() -> ! { 24 | if let (Some(mut p), Some(cp)) = (pac::Peripherals::take(), Peripherals::take()) { 25 | cortex_m::interrupt::free(move |cs| { 26 | let mut rcc = p.RCC.configure().sysclk(48.mhz()).freeze(&mut p.FLASH); 27 | 28 | // Get access to individual pins in the GPIO port 29 | let gpioa = p.GPIOB.split(&mut rcc); 30 | 31 | // (Re-)configure the pin connected to our LED as output 32 | let led = gpioa.pb3.into_push_pull_output(cs); 33 | 34 | // Transfer GPIO into a shared structure 35 | swap(&mut Some(led), &mut GPIO.borrow(cs).borrow_mut()); 36 | 37 | let mut syst = cp.SYST; 38 | 39 | // Initialise SysTick counter with a defined value 40 | unsafe { syst.cvr.write(1) }; 41 | 42 | // Set source for SysTick counter, here full operating frequency (== 48MHz) 43 | syst.set_clock_source(Core); 44 | 45 | // Set reload value, i.e. timer delay 48 MHz/4 Mcounts == 12Hz or 83ms 46 | syst.set_reload(4_000_000 - 1); 47 | 48 | // Start counting 49 | syst.enable_counter(); 50 | 51 | // Enable interrupt generation 52 | syst.enable_interrupt(); 53 | }); 54 | } 55 | 56 | loop { 57 | continue; 58 | } 59 | } 60 | 61 | // Define an exception handler, i.e. function to call when exception occurs. Here, if our SysTick 62 | // timer generates an exception the following handler will be called 63 | #[exception] 64 | fn SysTick() { 65 | // Our moved LED pin 66 | static mut LED: Option = None; 67 | 68 | // Exception handler state variable 69 | static mut STATE: u8 = 1; 70 | 71 | // If LED pin was moved into the exception handler, just use it 72 | if let Some(led) = LED { 73 | // Check state variable, keep LED off most of the time and turn it on every 10th tick 74 | if *STATE < 10 { 75 | // Turn off the LED 76 | led.set_low().ok(); 77 | 78 | // And now increment state variable 79 | *STATE += 1; 80 | } else { 81 | // Turn on the LED 82 | led.set_high().ok(); 83 | 84 | // And set new state variable back to 0 85 | *STATE = 0; 86 | } 87 | } 88 | // Otherwise move it out of the Mutex protected shared region into our exception handler 89 | else { 90 | // Enter critical section 91 | cortex_m::interrupt::free(|cs| { 92 | // Swap globally stored data with SysTick private data 93 | swap(LED, &mut GPIO.borrow(cs).borrow_mut()); 94 | }); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /examples/i2c_find_address.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{i2c::I2c, pac, prelude::*}; 9 | 10 | use cortex_m_rt::entry; 11 | 12 | /* Example meant for stm32f030xc MCUs with i2c devices connected on PB7 and PB8 */ 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | if let Some(p) = pac::Peripherals::take() { 17 | cortex_m::interrupt::free(move |cs| { 18 | let mut flash = p.FLASH; 19 | let mut rcc = p.RCC.configure().freeze(&mut flash); 20 | 21 | let gpiob = p.GPIOB.split(&mut rcc); 22 | 23 | // Configure pins for I2C 24 | let sda = gpiob.pb7.into_alternate_af1(cs); 25 | let scl = gpiob.pb8.into_alternate_af1(cs); 26 | 27 | // Configure I2C with 100kHz rate 28 | let mut i2c = I2c::i2c1(p.I2C1, (scl, sda), 100.khz(), &mut rcc); 29 | 30 | let mut _devices = 0; 31 | // I2C addresses are 7-bit wide, covering the 0-127 range 32 | for add in 0..=127 { 33 | // The write method sends the specified address and checks for acknowledgement; 34 | // if no ack is given by the slave device the result is Err(), otherwise Ok() 35 | // Since we only care for an acknowledgement the data sent can be empty 36 | if i2c.write(add, &[]).is_ok() { 37 | _devices += 1; 38 | } 39 | } 40 | 41 | // Here the variable "_devices" counts how many i2c addresses were a hit 42 | }); 43 | } 44 | 45 | loop { 46 | continue; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/led_hal_button_irq.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{ 9 | delay::Delay, 10 | gpio::*, 11 | pac::{interrupt, Interrupt, Peripherals, EXTI}, 12 | prelude::*, 13 | }; 14 | 15 | use cortex_m::{interrupt::Mutex, peripheral::Peripherals as c_m_Peripherals}; 16 | use cortex_m_rt::entry; 17 | 18 | use core::{cell::RefCell, ops::DerefMut}; 19 | 20 | // Make our LED globally available 21 | static LED: Mutex>>>> = Mutex::new(RefCell::new(None)); 22 | 23 | // Make our delay provider globally available 24 | static DELAY: Mutex>> = Mutex::new(RefCell::new(None)); 25 | 26 | // Make external interrupt registers globally available 27 | static INT: Mutex>> = Mutex::new(RefCell::new(None)); 28 | 29 | #[entry] 30 | fn main() -> ! { 31 | if let (Some(p), Some(cp)) = (Peripherals::take(), c_m_Peripherals::take()) { 32 | cortex_m::interrupt::free(move |cs| { 33 | // Enable clock for SYSCFG 34 | let rcc = p.RCC; 35 | rcc.apb2enr.modify(|_, w| w.syscfgen().set_bit()); 36 | 37 | let mut flash = p.FLASH; 38 | let mut rcc = rcc.configure().sysclk(8.mhz()).freeze(&mut flash); 39 | 40 | let gpioa = p.GPIOA.split(&mut rcc); 41 | let gpiob = p.GPIOB.split(&mut rcc); 42 | let syscfg = p.SYSCFG; 43 | let exti = p.EXTI; 44 | 45 | // Configure PB1 as input (button) 46 | let _ = gpiob.pb1.into_pull_down_input(cs); 47 | 48 | // Configure PA1 as output (LED) 49 | let mut led = gpioa.pa1.into_push_pull_output(cs); 50 | 51 | // Turn off LED 52 | led.set_low().ok(); 53 | 54 | // Initialise delay provider 55 | let delay = Delay::new(cp.SYST, &rcc); 56 | 57 | // Enable external interrupt for PB1 58 | syscfg.exticr1.modify(|_, w| unsafe { w.exti1().bits(1) }); 59 | 60 | // Set interrupt request mask for line 1 61 | exti.imr.modify(|_, w| w.mr1().set_bit()); 62 | 63 | // Set interrupt rising trigger for line 1 64 | exti.rtsr.modify(|_, w| w.tr1().set_bit()); 65 | 66 | // Move control over LED and DELAY and EXTI into global mutexes 67 | *LED.borrow(cs).borrow_mut() = Some(led); 68 | *DELAY.borrow(cs).borrow_mut() = Some(delay); 69 | *INT.borrow(cs).borrow_mut() = Some(exti); 70 | 71 | // Enable EXTI IRQ, set prio 1 and clear any pending IRQs 72 | let mut nvic = cp.NVIC; 73 | unsafe { 74 | nvic.set_priority(Interrupt::EXTI0_1, 1); 75 | cortex_m::peripheral::NVIC::unmask(Interrupt::EXTI0_1); 76 | } 77 | cortex_m::peripheral::NVIC::unpend(Interrupt::EXTI0_1); 78 | }); 79 | } 80 | 81 | loop { 82 | continue; 83 | } 84 | } 85 | 86 | // Define an interupt handler, i.e. function to call when interrupt occurs. Here if our external 87 | // interrupt trips when the button is pressed and will light the LED for a second 88 | #[interrupt] 89 | fn EXTI0_1() { 90 | // Enter critical section 91 | cortex_m::interrupt::free(|cs| { 92 | // Obtain all Mutex protected resources 93 | if let (&mut Some(ref mut led), &mut Some(ref mut delay), &mut Some(ref mut exti)) = ( 94 | LED.borrow(cs).borrow_mut().deref_mut(), 95 | DELAY.borrow(cs).borrow_mut().deref_mut(), 96 | INT.borrow(cs).borrow_mut().deref_mut(), 97 | ) { 98 | // Turn on LED 99 | led.set_high().ok(); 100 | 101 | // Wait a second 102 | delay.delay_ms(1_000_u16); 103 | 104 | // Turn off LED 105 | led.set_low().ok(); 106 | 107 | // Clear event triggering the interrupt 108 | exti.pr.write(|w| w.pif1().set_bit()); 109 | } 110 | }); 111 | } 112 | -------------------------------------------------------------------------------- /examples/pwm.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![no_main] 3 | #![no_std] 4 | 5 | // Halt on panic 6 | use panic_halt as _; 7 | 8 | use cortex_m_rt::entry; 9 | 10 | use stm32f0xx_hal as hal; 11 | 12 | use hal::{pac, prelude::*, pwm}; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | if let Some(mut dp) = pac::Peripherals::take() { 17 | // Set up the system clock. 18 | let mut rcc = dp.RCC.configure().sysclk(8.mhz()).freeze(&mut dp.FLASH); 19 | 20 | let gpioa = dp.GPIOA.split(&mut rcc); 21 | let channels = cortex_m::interrupt::free(move |cs| { 22 | ( 23 | gpioa.pa8.into_alternate_af2(cs), 24 | gpioa.pa9.into_alternate_af2(cs), 25 | ) 26 | }); 27 | 28 | let pwm = pwm::tim1(dp.TIM1, channels, &mut rcc, 20u32.khz()); 29 | let (mut ch1, _ch2) = pwm; 30 | let max_duty = ch1.get_max_duty(); 31 | ch1.set_duty(max_duty / 2); 32 | ch1.enable(); 33 | } 34 | 35 | loop { 36 | cortex_m::asm::nop(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/pwm_complementary.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![no_main] 3 | #![no_std] 4 | 5 | // Halt on panic 6 | use panic_halt as _; 7 | 8 | use cortex_m_rt::entry; 9 | 10 | use stm32f0xx_hal as hal; 11 | 12 | use hal::{delay::Delay, pac, prelude::*, pwm}; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | if let Some(mut dp) = pac::Peripherals::take() { 17 | // Set up the system clock. 18 | let mut rcc = dp.RCC.configure().sysclk(8.mhz()).freeze(&mut dp.FLASH); 19 | 20 | let gpioa = dp.GPIOA.split(&mut rcc); 21 | let channels = cortex_m::interrupt::free(move |cs| { 22 | ( 23 | gpioa.pa8.into_alternate_af2(cs), // on TIM1_CH1 24 | gpioa.pa7.into_alternate_af2(cs), // on TIM1_CH1N 25 | ) 26 | }); 27 | 28 | let pwm = pwm::tim1(dp.TIM1, channels, &mut rcc, 20u32.khz()); 29 | let (mut ch1, mut ch1n) = pwm; 30 | let max_duty = ch1.get_max_duty(); 31 | ch1.set_duty(max_duty / 2); 32 | ch1.enable(); 33 | ch1n.enable(); 34 | 35 | // simple duty sweep 36 | if let Some(cp) = cortex_m::Peripherals::take() { 37 | let mut delay = Delay::new(cp.SYST, &rcc); 38 | 39 | let steps = 100; 40 | 41 | loop { 42 | for i in 0..steps { 43 | ch1.set_duty(max_duty / steps * i); 44 | delay.delay_ms(30u16); 45 | } 46 | 47 | for i in (1..steps).rev() { 48 | ch1.set_duty(max_duty / steps * i); 49 | delay.delay_ms(30u16); 50 | } 51 | } 52 | } 53 | } 54 | 55 | // something went wrong when acquiring peripheral access 56 | loop { 57 | cortex_m::asm::nop(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/serial_echo.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{pac, prelude::*, serial::Serial}; 9 | 10 | use cortex_m_rt::entry; 11 | 12 | #[entry] 13 | fn main() -> ! { 14 | if let Some(p) = pac::Peripherals::take() { 15 | let mut flash = p.FLASH; 16 | let mut rcc = p.RCC.configure().sysclk(48.mhz()).freeze(&mut flash); 17 | 18 | let gpioa = p.GPIOA.split(&mut rcc); 19 | 20 | let (tx, rx) = cortex_m::interrupt::free(move |cs| { 21 | ( 22 | gpioa.pa9.into_alternate_af1(cs), 23 | gpioa.pa10.into_alternate_af1(cs), 24 | ) 25 | }); 26 | 27 | let mut serial = Serial::usart1(p.USART1, (tx, rx), 115_200.bps(), &mut rcc); 28 | 29 | loop { 30 | // Wait for reception of a single byte 31 | let received = nb::block!(serial.read()).unwrap(); 32 | 33 | // Send back previously received byte and wait for completion 34 | nb::block!(serial.write(received)).ok(); 35 | } 36 | } 37 | 38 | loop { 39 | continue; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/serial_spi_bridge.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{ 9 | pac, 10 | prelude::*, 11 | serial::Serial, 12 | spi::Spi, 13 | spi::{Mode, Phase, Polarity}, 14 | }; 15 | 16 | use nb::block; 17 | 18 | use cortex_m_rt::entry; 19 | 20 | /// A basic serial to spi example 21 | /// 22 | /// If you connect MOSI & MISO pins together, you'll see all characters 23 | /// that you typed in your serial terminal echoed back 24 | /// 25 | /// If you connect MISO to GND, you'll see nothing coming back 26 | #[entry] 27 | fn main() -> ! { 28 | const MODE: Mode = Mode { 29 | polarity: Polarity::IdleHigh, 30 | phase: Phase::CaptureOnSecondTransition, 31 | }; 32 | 33 | if let Some(p) = pac::Peripherals::take() { 34 | let mut flash = p.FLASH; 35 | let mut rcc = p.RCC.configure().freeze(&mut flash); 36 | 37 | let gpioa = p.GPIOA.split(&mut rcc); 38 | 39 | let (sck, miso, mosi, tx, rx) = cortex_m::interrupt::free(move |cs| { 40 | ( 41 | // SPI pins 42 | gpioa.pa5.into_alternate_af0(cs), 43 | gpioa.pa6.into_alternate_af0(cs), 44 | gpioa.pa7.into_alternate_af0(cs), 45 | // USART pins 46 | gpioa.pa9.into_alternate_af1(cs), 47 | gpioa.pa10.into_alternate_af1(cs), 48 | ) 49 | }); 50 | 51 | // Configure SPI with 1MHz rate 52 | let mut spi = Spi::spi1(p.SPI1, (sck, miso, mosi), MODE, 1.mhz(), &mut rcc); 53 | 54 | let serial = Serial::usart1(p.USART1, (tx, rx), 115_200.bps(), &mut rcc); 55 | 56 | let (mut tx, mut rx) = serial.split(); 57 | 58 | let mut data = [0]; 59 | loop { 60 | let serial_received = block!(rx.read()).unwrap(); 61 | spi.write(&[serial_received]).ok(); 62 | let spi_received = spi.transfer(&mut data).unwrap(); 63 | block!(tx.write(spi_received[0])).ok(); 64 | } 65 | } 66 | 67 | loop { 68 | continue; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /examples/serial_stopwatch.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{ 9 | pac::{interrupt, Interrupt, Peripherals, TIM7}, 10 | prelude::*, 11 | serial::Serial, 12 | timers::{Event, Timer}, 13 | }; 14 | use core::cell::RefCell; 15 | use core::fmt::Write as _; 16 | use core::ops::DerefMut; 17 | 18 | use cortex_m::{interrupt::Mutex, peripheral::Peripherals as c_m_Peripherals}; 19 | use cortex_m_rt::entry; 20 | 21 | // Make timer interrupt registers globally available 22 | static GINT: Mutex>>> = Mutex::new(RefCell::new(None)); 23 | 24 | #[derive(Copy, Clone)] 25 | struct Time { 26 | seconds: u32, 27 | millis: u16, 28 | } 29 | 30 | static TIME: Mutex> = Mutex::new(RefCell::new(Time { 31 | seconds: 0, 32 | millis: 0, 33 | })); 34 | 35 | // Define an interupt handler, i.e. function to call when interrupt occurs. Here if our external 36 | // interrupt trips when the timer timed out 37 | #[interrupt] 38 | fn TIM7() { 39 | cortex_m::interrupt::free(|cs| { 40 | // Move LED pin here, leaving a None in its place 41 | GINT.borrow(cs) 42 | .borrow_mut() 43 | .deref_mut() 44 | .as_mut() 45 | .unwrap() 46 | .wait() 47 | .ok(); 48 | let mut time = TIME.borrow(cs).borrow_mut(); 49 | time.millis += 1; 50 | if time.millis == 1000 { 51 | time.millis = 0; 52 | time.seconds += 1; 53 | } 54 | }); 55 | } 56 | 57 | #[entry] 58 | fn main() -> ! { 59 | if let (Some(p), Some(cp)) = (Peripherals::take(), c_m_Peripherals::take()) { 60 | let mut serial = cortex_m::interrupt::free(move |cs| { 61 | let mut flash = p.FLASH; 62 | let mut rcc = p.RCC.configure().sysclk(48.mhz()).freeze(&mut flash); 63 | 64 | // Use USART2 with PA2 and PA3 as serial port 65 | let gpioa = p.GPIOA.split(&mut rcc); 66 | let tx = gpioa.pa2.into_alternate_af1(cs); 67 | let rx = gpioa.pa3.into_alternate_af1(cs); 68 | 69 | // Set up a timer expiring every millisecond 70 | let mut timer = Timer::tim7(p.TIM7, 1000.hz(), &mut rcc); 71 | 72 | // Generate an interrupt when the timer expires 73 | timer.listen(Event::TimeOut); 74 | 75 | // Move the timer into our global storage 76 | *GINT.borrow(cs).borrow_mut() = Some(timer); 77 | 78 | // Enable TIM7 IRQ, set prio 1 and clear any pending IRQs 79 | let mut nvic = cp.NVIC; 80 | unsafe { 81 | nvic.set_priority(Interrupt::TIM7, 1); 82 | cortex_m::peripheral::NVIC::unmask(Interrupt::TIM7); 83 | } 84 | cortex_m::peripheral::NVIC::unpend(Interrupt::TIM7); 85 | 86 | // Set up our serial port 87 | Serial::usart2(p.USART2, (tx, rx), 115_200.bps(), &mut rcc) 88 | }); 89 | 90 | // Print a welcome message 91 | writeln!( 92 | serial, 93 | "Welcome to the stop watch, hit any key to see the current value and 0 to reset\r", 94 | ) 95 | .ok(); 96 | 97 | loop { 98 | // Wait for reception of a single byte 99 | let received = nb::block!(serial.read()).unwrap(); 100 | 101 | let time = cortex_m::interrupt::free(|cs| { 102 | let mut time = TIME.borrow(cs).borrow_mut(); 103 | 104 | // If we received a 0, reset the time 105 | if received == b'0' { 106 | time.millis = 0; 107 | time.seconds = 0; 108 | } 109 | 110 | *time 111 | }); 112 | 113 | // Print the current time 114 | writeln!(serial, "{}.{:03}s\r", time.seconds, time.millis).ok(); 115 | } 116 | } 117 | 118 | loop { 119 | continue; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /examples/spi_hal_apa102c.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{ 9 | pac, 10 | prelude::*, 11 | spi::Spi, 12 | spi::{Mode, Phase, Polarity}, 13 | }; 14 | 15 | use cortex_m_rt::entry; 16 | 17 | #[entry] 18 | fn main() -> ! { 19 | const MODE: Mode = Mode { 20 | polarity: Polarity::IdleHigh, 21 | phase: Phase::CaptureOnSecondTransition, 22 | }; 23 | 24 | if let Some(p) = pac::Peripherals::take() { 25 | let mut flash = p.FLASH; 26 | let mut rcc = p.RCC.configure().freeze(&mut flash); 27 | 28 | let gpioa = p.GPIOA.split(&mut rcc); 29 | 30 | // Configure pins for SPI 31 | let (sck, miso, mosi) = cortex_m::interrupt::free(move |cs| { 32 | ( 33 | gpioa.pa5.into_alternate_af0(cs), 34 | gpioa.pa6.into_alternate_af0(cs), 35 | gpioa.pa7.into_alternate_af0(cs), 36 | ) 37 | }); 38 | 39 | // Configure SPI with 100kHz rate 40 | let mut spi = Spi::spi1(p.SPI1, (sck, miso, mosi), MODE, 100_000.hz(), &mut rcc); 41 | 42 | // Cycle through colors on 16 chained APA102C LEDs 43 | loop { 44 | for r in 0..255 { 45 | let _ = spi.write(&[0, 0, 0, 0]); 46 | for _i in 0..16 { 47 | let _ = spi.write(&[0b1110_0001, 0, 0, r]); 48 | } 49 | let _ = spi.write(&[0xFF, 0xFF, 0xFF, 0xFF]); 50 | } 51 | for b in 0..255 { 52 | let _ = spi.write(&[0, 0, 0, 0]); 53 | for _i in 0..16 { 54 | let _ = spi.write(&[0b1110_0001, b, 0, 0]); 55 | } 56 | let _ = spi.write(&[0xFF, 0xFF, 0xFF, 0xFF]); 57 | } 58 | for g in 0..255 { 59 | let _ = spi.write(&[0, 0, 0, 0]); 60 | for _i in 0..16 { 61 | let _ = spi.write(&[0b1110_0001, 0, g, 0]); 62 | } 63 | let _ = spi.write(&[0xFF, 0xFF, 0xFF, 0xFF]); 64 | } 65 | } 66 | } 67 | 68 | loop { 69 | continue; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/usb_serial.rs: -------------------------------------------------------------------------------- 1 | //! CDC-ACM serial port example using polling in a busy loop. 2 | //! Target board: NUCLEO-F042K6 3 | #![no_std] 4 | #![no_main] 5 | 6 | extern crate panic_halt; 7 | 8 | use cortex_m_rt::entry; 9 | use stm32f0xx_hal::usb::{Peripheral, UsbBus}; 10 | use stm32f0xx_hal::{pac, prelude::*}; 11 | use usb_device::prelude::*; 12 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | let mut dp = pac::Peripherals::take().unwrap(); 17 | 18 | /* 19 | * IMPORTANT: if you have a chip in TSSOP20 (STM32F042F) or UFQFPN28 (STM32F042G) package, 20 | * and want to use USB, make sure you call `remap_pins(rcc, syscfg)`, otherwise the device will not enumerate. 21 | * 22 | * Uncomment the following function if the situation above applies to you. 23 | */ 24 | 25 | // stm32f0xx_hal::usb::remap_pins(&mut dp.RCC, &mut dp.SYSCFG); 26 | 27 | let mut rcc = dp 28 | .RCC 29 | .configure() 30 | .hsi48() 31 | .enable_crs(dp.CRS) 32 | .sysclk(48.mhz()) 33 | .pclk(24.mhz()) 34 | .freeze(&mut dp.FLASH); 35 | 36 | // Configure the on-board LED (LD3, green) 37 | let gpiob = dp.GPIOB.split(&mut rcc); 38 | let mut led = cortex_m::interrupt::free(|cs| gpiob.pb3.into_push_pull_output(cs)); 39 | led.set_low().ok(); // Turn off 40 | 41 | let gpioa = dp.GPIOA.split(&mut rcc); 42 | 43 | let usb = Peripheral { 44 | usb: dp.USB, 45 | pin_dm: gpioa.pa11, 46 | pin_dp: gpioa.pa12, 47 | }; 48 | 49 | let usb_bus = UsbBus::new(usb); 50 | 51 | let mut serial = SerialPort::new(&usb_bus); 52 | 53 | let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) 54 | .manufacturer("Fake company") 55 | .product("Serial port") 56 | .serial_number("TEST") 57 | .device_class(USB_CLASS_CDC) 58 | .build(); 59 | 60 | loop { 61 | if !usb_dev.poll(&mut [&mut serial]) { 62 | continue; 63 | } 64 | 65 | let mut buf = [0u8; 64]; 66 | 67 | match serial.read(&mut buf) { 68 | Ok(count) if count > 0 => { 69 | led.set_high().ok(); // Turn on 70 | 71 | // Echo back in upper case 72 | for c in buf[0..count].iter_mut() { 73 | if 0x61 <= *c && *c <= 0x7a { 74 | *c &= !0x20; 75 | } 76 | } 77 | 78 | let mut write_offset = 0; 79 | while write_offset < count { 80 | match serial.write(&buf[write_offset..count]) { 81 | Ok(len) if len > 0 => { 82 | write_offset += len; 83 | } 84 | _ => {} 85 | } 86 | } 87 | } 88 | _ => {} 89 | } 90 | 91 | led.set_low().ok(); // Turn off 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /examples/watchdog.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f0xx_hal as hal; 7 | 8 | use crate::hal::{delay::Delay, pac, prelude::*, serial::Serial, time::Hertz, watchdog::Watchdog}; 9 | 10 | use cortex_m::peripheral::Peripherals; 11 | use cortex_m_rt::entry; 12 | 13 | use core::fmt::Write; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | if let (Some(p), Some(cp)) = (pac::Peripherals::take(), Peripherals::take()) { 18 | let mut flash = p.FLASH; 19 | let mut rcc = p.RCC.configure().sysclk(8.mhz()).freeze(&mut flash); 20 | 21 | let gpioa = p.GPIOA.split(&mut rcc); 22 | let dbgmcu = p.DBGMCU; 23 | 24 | // Disable the watchdog when the cpu is stopped under debug 25 | dbgmcu.apb1_fz.modify(|_, w| w.dbg_iwdg_stop().set_bit()); 26 | 27 | let mut watchdog = Watchdog::new(p.IWDG); 28 | 29 | // Get delay provider 30 | let mut delay = Delay::new(cp.SYST, &rcc); 31 | 32 | // Configure serial TX pin 33 | let tx = cortex_m::interrupt::free(move |cs| gpioa.pa9.into_alternate_af1(cs)); 34 | 35 | // Obtain a serial peripheral with for unidirectional communication 36 | let mut serial = Serial::usart1tx(p.USART1, tx, 115_200.bps(), &mut rcc); 37 | 38 | serial.write_str("RESET \r\n").ok(); 39 | 40 | watchdog.start(Hertz(1)); 41 | delay.delay_ms(500_u16); 42 | watchdog.feed(); 43 | delay.delay_ms(500_u16); 44 | watchdog.feed(); 45 | delay.delay_ms(500_u16); 46 | serial.write_str("This will get printed \r\n").ok(); 47 | watchdog.feed(); 48 | 49 | // Now a reset happens while delaying 50 | delay.delay_ms(1500_u16); 51 | serial.write_str("This won't\r\n").ok(); 52 | } 53 | 54 | loop { 55 | continue; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 16K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 4K 6 | } 7 | 8 | /* This is where the call stack will be allocated. */ 9 | /* The stack is of the full descending type. */ 10 | /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ 11 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); 12 | -------------------------------------------------------------------------------- /openocd.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/cmsis-dap.cfg] 2 | source [find target/stm32f0x.cfg] 3 | 4 | init 5 | flash probe 0 6 | -------------------------------------------------------------------------------- /openocd_program.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if (( $# != 1 )); then 3 | echo "Usage:" 4 | echo "$0 " 5 | exit 1 6 | fi 7 | 8 | openocd -f openocd.cfg -c "program $1 verify reset exit" 9 | -------------------------------------------------------------------------------- /src/adc.rs: -------------------------------------------------------------------------------- 1 | //! # API for the Analog to Digital converter 2 | //! 3 | //! Currently implements oneshot conversion with variable sampling times. 4 | //! Also references for the internal temperature sense, voltage 5 | //! reference and battery sense are provided. 6 | //! 7 | //! ## Example 8 | //! ``` no_run 9 | //! use stm32f0xx_hal as hal; 10 | //! 11 | //! use crate::hal::pac; 12 | //! use crate::hal::prelude::*; 13 | //! use crate::hal::adc::Adc; 14 | //! 15 | //! cortex_m::interrupt::free(|cs| { 16 | //! let mut p = pac::Peripherals::take().unwrap(); 17 | //! let mut rcc = p.RCC.configure().freeze(&mut p.FLASH); 18 | //! 19 | //! let gpioa = p.GPIOA.split(&mut rcc); 20 | //! 21 | //! let mut led = gpioa.pa1.into_push_pull_pull_output(cs); 22 | //! let mut an_in = gpioa.pa0.into_analog(cs); 23 | //! 24 | //! let mut delay = Delay::new(cp.SYST, &rcc); 25 | //! 26 | //! let mut adc = Adc::new(p.ADC, &mut rcc); 27 | //! 28 | //! loop { 29 | //! let val: u16 = adc.read(&mut an_in).unwrap(); 30 | //! if val < ((1 << 8) - 1) { 31 | //! led.set_low(); 32 | //! } else { 33 | //! led.set_high(); 34 | //! } 35 | //! delay.delay_ms(50_u16); 36 | //! } 37 | //! }); 38 | //! ``` 39 | 40 | const VREFCAL: *const u16 = 0x1FFF_F7BA as *const u16; 41 | const VTEMPCAL30: *const u16 = 0x1FFF_F7B8 as *const u16; 42 | const VTEMPCAL110: *const u16 = 0x1FFF_F7C2 as *const u16; 43 | const VDD_CALIB: u16 = 3300; 44 | 45 | use core::{default, ptr}; 46 | 47 | use embedded_hal::{ 48 | adc::{Channel, OneShot}, 49 | blocking::delay::DelayUs, 50 | }; 51 | 52 | use crate::{ 53 | delay::Delay, 54 | gpio::*, 55 | pac::{ 56 | adc::{ 57 | cfgr1::{ALIGN_A, RES_A}, 58 | smpr::SMP_A, 59 | }, 60 | ADC, 61 | }, 62 | rcc::Rcc, 63 | }; 64 | 65 | /// Analog to Digital converter interface 66 | pub struct Adc { 67 | rb: ADC, 68 | sample_time: AdcSampleTime, 69 | align: AdcAlign, 70 | precision: AdcPrecision, 71 | } 72 | 73 | #[derive(Clone, Copy, Debug, PartialEq)] 74 | /// ADC Sampling time 75 | /// 76 | /// Options for the sampling time, each is T + 0.5 ADC clock cycles. 77 | pub enum AdcSampleTime { 78 | /// 1.5 cycles sampling time 79 | T_1, 80 | /// 7.5 cycles sampling time 81 | T_7, 82 | /// 13.5 cycles sampling time 83 | T_13, 84 | /// 28.5 cycles sampling time 85 | T_28, 86 | /// 41.5 cycles sampling time 87 | T_41, 88 | /// 55.5 cycles sampling time 89 | T_55, 90 | /// 71.5 cycles sampling time 91 | T_71, 92 | /// 239.5 cycles sampling time 93 | T_239, 94 | } 95 | 96 | impl default::Default for AdcSampleTime { 97 | /// Get the default sample time (currently 239.5 cycles) 98 | fn default() -> Self { 99 | AdcSampleTime::T_239 100 | } 101 | } 102 | 103 | impl From for SMP_A { 104 | fn from(val: AdcSampleTime) -> Self { 105 | match val { 106 | AdcSampleTime::T_1 => SMP_A::Cycles15, 107 | AdcSampleTime::T_7 => SMP_A::Cycles75, 108 | AdcSampleTime::T_13 => SMP_A::Cycles135, 109 | AdcSampleTime::T_28 => SMP_A::Cycles285, 110 | AdcSampleTime::T_41 => SMP_A::Cycles415, 111 | AdcSampleTime::T_55 => SMP_A::Cycles555, 112 | AdcSampleTime::T_71 => SMP_A::Cycles715, 113 | AdcSampleTime::T_239 => SMP_A::Cycles2395, 114 | } 115 | } 116 | } 117 | 118 | #[derive(Clone, Copy, Debug, PartialEq)] 119 | /// ADC Result Alignment 120 | pub enum AdcAlign { 121 | /// Left aligned results (most significant bits) 122 | /// 123 | /// Results in all precisions returning a value in the range 0-65535. 124 | /// Depending on the precision the result will step by larger or smaller 125 | /// amounts. 126 | Left, 127 | /// Right aligned results (least significant bits) 128 | /// 129 | /// Results in all precisions returning values from 0-(2^bits-1) in 130 | /// steps of 1. 131 | Right, 132 | /// Left aligned results without correction of 6bit values. 133 | /// 134 | /// Returns left aligned results exactly as shown in RM0091 Fig.37. 135 | /// Where the values are left aligned within the u16, with the exception 136 | /// of 6 bit mode where the value is left aligned within the first byte of 137 | /// the u16. 138 | LeftAsRM, 139 | } 140 | 141 | impl default::Default for AdcAlign { 142 | /// Get the default alignment (currently right aligned) 143 | fn default() -> Self { 144 | AdcAlign::Right 145 | } 146 | } 147 | 148 | impl From for ALIGN_A { 149 | fn from(val: AdcAlign) -> Self { 150 | match val { 151 | AdcAlign::Left => ALIGN_A::Left, 152 | AdcAlign::Right => ALIGN_A::Right, 153 | AdcAlign::LeftAsRM => ALIGN_A::Left, 154 | } 155 | } 156 | } 157 | 158 | #[derive(Clone, Copy, Debug, PartialEq)] 159 | /// ADC Sampling Precision 160 | pub enum AdcPrecision { 161 | /// 12 bit precision 162 | B_12, 163 | /// 10 bit precision 164 | B_10, 165 | /// 8 bit precision 166 | B_8, 167 | /// 6 bit precision 168 | B_6, 169 | } 170 | 171 | impl default::Default for AdcPrecision { 172 | /// Get the default precision (currently 12 bit precision) 173 | fn default() -> Self { 174 | AdcPrecision::B_12 175 | } 176 | } 177 | 178 | impl From for RES_A { 179 | fn from(val: AdcPrecision) -> Self { 180 | match val { 181 | AdcPrecision::B_12 => RES_A::TwelveBit, 182 | AdcPrecision::B_10 => RES_A::TenBit, 183 | AdcPrecision::B_8 => RES_A::EightBit, 184 | AdcPrecision::B_6 => RES_A::SixBit, 185 | } 186 | } 187 | } 188 | 189 | macro_rules! adc_pins { 190 | ($($pin:ty => $chan:expr),+ $(,)*) => { 191 | $( 192 | impl Channel for $pin { 193 | type ID = u8; 194 | 195 | fn channel() -> u8 { $chan } 196 | } 197 | )+ 198 | }; 199 | } 200 | 201 | adc_pins!( 202 | gpioa::PA0 => 0_u8, 203 | gpioa::PA1 => 1_u8, 204 | gpioa::PA2 => 2_u8, 205 | gpioa::PA3 => 3_u8, 206 | gpioa::PA4 => 4_u8, 207 | gpioa::PA5 => 5_u8, 208 | gpioa::PA6 => 6_u8, 209 | gpioa::PA7 => 7_u8, 210 | gpiob::PB0 => 8_u8, 211 | gpiob::PB1 => 9_u8, 212 | ); 213 | 214 | #[cfg(any( 215 | feature = "stm32f030", 216 | feature = "stm32f051", 217 | feature = "stm32f058", 218 | feature = "stm32f070", 219 | feature = "stm32f071", 220 | feature = "stm32f072", 221 | feature = "stm32f078", 222 | feature = "stm32f091", 223 | feature = "stm32f098", 224 | ))] 225 | adc_pins!( 226 | gpioc::PC0 => 10_u8, 227 | gpioc::PC1 => 11_u8, 228 | gpioc::PC2 => 12_u8, 229 | gpioc::PC3 => 13_u8, 230 | gpioc::PC4 => 14_u8, 231 | gpioc::PC5 => 15_u8, 232 | ); 233 | 234 | #[derive(Debug, Default)] 235 | /// Internal temperature sensor (ADC Channel 16) 236 | pub struct VTemp; 237 | 238 | #[derive(Debug, Default)] 239 | /// Internal voltage reference (ADC Channel 17) 240 | pub struct VRef; 241 | 242 | adc_pins!( 243 | VTemp => 16_u8, 244 | VRef => 17_u8, 245 | ); 246 | 247 | impl VTemp { 248 | /// Init a new VTemp 249 | pub fn new() -> Self { 250 | Self {} 251 | } 252 | 253 | /// Enable the internal temperature sense, this has a wake up time 254 | /// tSTART which can be found in your micro's datasheet, you 255 | /// must wait at least that long after enabling before taking a reading. 256 | /// Remember to disable when not in use. 257 | pub fn enable(&mut self, adc: &mut Adc) { 258 | adc.rb.ccr.modify(|_, w| w.tsen().set_bit()); 259 | } 260 | 261 | /// Disable the internal temperature sense. 262 | pub fn disable(&mut self, adc: &mut Adc) { 263 | adc.rb.ccr.modify(|_, w| w.tsen().clear_bit()); 264 | } 265 | 266 | /// Checks if the temperature sensor is enabled, does not account for the 267 | /// tSTART time however. 268 | pub fn is_enabled(&self, adc: &Adc) -> bool { 269 | adc.rb.ccr.read().tsen().bit_is_set() 270 | } 271 | 272 | fn convert_temp(vtemp: u16, vdda: u16) -> i16 { 273 | let vtemp30_cal = unsafe { ptr::read(VTEMPCAL30) } as i32; 274 | let vtemp110_cal = unsafe { ptr::read(VTEMPCAL110) } as i32; 275 | let raw_temp_comp = vtemp as u32 * vdda as u32 / VDD_CALIB as u32; 276 | ((raw_temp_comp as i32 - vtemp30_cal) * 10 * (110 - 30) / (vtemp110_cal - vtemp30_cal) 277 | + 300) as i16 278 | } 279 | 280 | /// Read the value of the internal temperature sensor and return the 281 | /// result in 10ths of a degree centigrade. 282 | /// 283 | /// Given a delay reference it will attempt to restrict to the 284 | /// minimum delay needed to ensure a 10 us tSTART value. 285 | /// Otherwise it will approximate the required delay using ADC reads. 286 | pub fn read(adc: &mut Adc, delay: Option<&mut Delay>) -> i16 { 287 | let mut vtemp = Self::new(); 288 | let vtemp_preenable = vtemp.is_enabled(adc); 289 | 290 | if !vtemp_preenable { 291 | vtemp.enable(adc); 292 | 293 | if let Some(dref) = delay { 294 | dref.delay_us(2_u16); 295 | } else { 296 | // Double read of vdda to allow sufficient startup time for the temp sensor 297 | VRef::read_vdda(adc); 298 | } 299 | } 300 | let vdda = VRef::read_vdda(adc); 301 | 302 | let prev_cfg = adc.default_cfg(); 303 | 304 | let vtemp_val = adc.read(&mut vtemp).unwrap(); 305 | 306 | if !vtemp_preenable { 307 | vtemp.disable(adc); 308 | } 309 | 310 | adc.restore_cfg(prev_cfg); 311 | 312 | Self::convert_temp(vtemp_val, vdda) 313 | } 314 | } 315 | 316 | impl VRef { 317 | /// Init a new VRef 318 | pub fn new() -> Self { 319 | Self {} 320 | } 321 | 322 | /// Enable the internal voltage reference, remember to disable when not in use. 323 | pub fn enable(&mut self, adc: &mut Adc) { 324 | adc.rb.ccr.modify(|_, w| w.vrefen().set_bit()); 325 | } 326 | 327 | /// Disable the internal reference voltage. 328 | pub fn disable(&mut self, adc: &mut Adc) { 329 | adc.rb.ccr.modify(|_, w| w.vrefen().clear_bit()); 330 | } 331 | 332 | /// Returns if the internal voltage reference is enabled. 333 | pub fn is_enabled(&self, adc: &Adc) -> bool { 334 | adc.rb.ccr.read().vrefen().bit_is_set() 335 | } 336 | 337 | /// Reads the value of VDDA in milli-volts 338 | pub fn read_vdda(adc: &mut Adc) -> u16 { 339 | let vrefint_cal = u32::from(unsafe { ptr::read(VREFCAL) }); 340 | let mut vref = Self::new(); 341 | 342 | let prev_cfg = adc.default_cfg(); 343 | 344 | let vref_val: u32 = if vref.is_enabled(adc) { 345 | adc.read(&mut vref).unwrap() 346 | } else { 347 | vref.enable(adc); 348 | 349 | let ret = adc.read(&mut vref).unwrap(); 350 | 351 | vref.disable(adc); 352 | ret 353 | }; 354 | 355 | adc.restore_cfg(prev_cfg); 356 | 357 | (u32::from(VDD_CALIB) * vrefint_cal / vref_val) as u16 358 | } 359 | } 360 | 361 | #[cfg(any( 362 | feature = "stm32f031", 363 | feature = "stm32f038", 364 | feature = "stm32f042", 365 | feature = "stm32f048", 366 | feature = "stm32f051", 367 | feature = "stm32f058", 368 | feature = "stm32f071", 369 | feature = "stm32f072", 370 | feature = "stm32f078", 371 | feature = "stm32f091", 372 | feature = "stm32f098", 373 | ))] 374 | #[derive(Debug, Default)] 375 | /// Battery reference voltage (ADC Channel 18) 376 | pub struct VBat; 377 | 378 | #[cfg(any( 379 | feature = "stm32f031", 380 | feature = "stm32f038", 381 | feature = "stm32f042", 382 | feature = "stm32f048", 383 | feature = "stm32f051", 384 | feature = "stm32f058", 385 | feature = "stm32f071", 386 | feature = "stm32f072", 387 | feature = "stm32f078", 388 | feature = "stm32f091", 389 | feature = "stm32f098", 390 | ))] 391 | adc_pins!( 392 | VBat => 18_u8, 393 | ); 394 | 395 | #[cfg(any( 396 | feature = "stm32f031", 397 | feature = "stm32f038", 398 | feature = "stm32f042", 399 | feature = "stm32f048", 400 | feature = "stm32f051", 401 | feature = "stm32f058", 402 | feature = "stm32f071", 403 | feature = "stm32f072", 404 | feature = "stm32f078", 405 | feature = "stm32f091", 406 | feature = "stm32f098", 407 | ))] 408 | impl VBat { 409 | /// Init a new VBat 410 | pub fn new() -> Self { 411 | Self {} 412 | } 413 | 414 | /// Enable the internal VBat sense, remember to disable when not in use 415 | /// as otherwise it will sap current from the VBat source. 416 | pub fn enable(&mut self, adc: &mut Adc) { 417 | adc.rb.ccr.modify(|_, w| w.vbaten().set_bit()); 418 | } 419 | 420 | /// Disable the internal VBat sense. 421 | pub fn disable(&mut self, adc: &mut Adc) { 422 | adc.rb.ccr.modify(|_, w| w.vbaten().clear_bit()); 423 | } 424 | 425 | /// Returns if the internal VBat sense is enabled 426 | pub fn is_enabled(&self, adc: &Adc) -> bool { 427 | adc.rb.ccr.read().vbaten().bit_is_set() 428 | } 429 | 430 | /// Reads the value of VBat in milli-volts 431 | pub fn read(adc: &mut Adc) -> u16 { 432 | let mut vbat = Self::new(); 433 | 434 | let vbat_val: u16 = if vbat.is_enabled(adc) { 435 | adc.read_abs_mv(&mut vbat) 436 | } else { 437 | vbat.enable(adc); 438 | 439 | let ret = adc.read_abs_mv(&mut vbat); 440 | 441 | vbat.disable(adc); 442 | ret 443 | }; 444 | 445 | vbat_val * 2 446 | } 447 | } 448 | 449 | /// A stored ADC config, can be restored by using the `Adc::restore_cfg` method 450 | #[derive(Copy, Clone, Debug, PartialEq)] 451 | pub struct StoredConfig(AdcSampleTime, AdcAlign, AdcPrecision); 452 | 453 | impl Adc { 454 | /// Init a new Adc 455 | /// 456 | /// Sets all configurable parameters to defaults, enables the HSI14 clock 457 | /// for the ADC if it is not already enabled and performs a boot time 458 | /// calibration. As such this method may take an appreciable time to run. 459 | pub fn new(adc: ADC, rcc: &mut Rcc) -> Self { 460 | let mut s = Self { 461 | rb: adc, 462 | sample_time: AdcSampleTime::default(), 463 | align: AdcAlign::default(), 464 | precision: AdcPrecision::default(), 465 | }; 466 | s.select_clock(rcc); 467 | s.calibrate(); 468 | s 469 | } 470 | 471 | /// Saves a copy of the current ADC config 472 | pub fn save_cfg(&mut self) -> StoredConfig { 473 | StoredConfig(self.sample_time, self.align, self.precision) 474 | } 475 | 476 | /// Restores a stored config 477 | pub fn restore_cfg(&mut self, cfg: StoredConfig) { 478 | self.sample_time = cfg.0; 479 | self.align = cfg.1; 480 | self.precision = cfg.2; 481 | } 482 | 483 | /// Resets the ADC config to default, returning the existing config as 484 | /// a stored config. 485 | pub fn default_cfg(&mut self) -> StoredConfig { 486 | let cfg = self.save_cfg(); 487 | self.sample_time = AdcSampleTime::default(); 488 | self.align = AdcAlign::default(); 489 | self.precision = AdcPrecision::default(); 490 | cfg 491 | } 492 | 493 | /// Set the Adc sampling time 494 | /// 495 | /// Options can be found in [AdcSampleTime](crate::adc::AdcSampleTime). 496 | pub fn set_sample_time(&mut self, t_samp: AdcSampleTime) { 497 | self.sample_time = t_samp; 498 | } 499 | 500 | /// Set the Adc result alignment 501 | /// 502 | /// Options can be found in [AdcAlign](crate::adc::AdcAlign). 503 | pub fn set_align(&mut self, align: AdcAlign) { 504 | self.align = align; 505 | } 506 | 507 | /// Set the Adc precision 508 | /// 509 | /// Options can be found in [AdcPrecision](crate::adc::AdcPrecision). 510 | pub fn set_precision(&mut self, precision: AdcPrecision) { 511 | self.precision = precision; 512 | } 513 | 514 | /// Returns the largest possible sample value for the current settings 515 | pub fn max_sample(&self) -> u16 { 516 | match self.align { 517 | AdcAlign::Left => u16::MAX, 518 | AdcAlign::LeftAsRM => match self.precision { 519 | AdcPrecision::B_6 => u16::from(u8::MAX), 520 | _ => u16::MAX, 521 | }, 522 | AdcAlign::Right => match self.precision { 523 | AdcPrecision::B_12 => (1 << 12) - 1, 524 | AdcPrecision::B_10 => (1 << 10) - 1, 525 | AdcPrecision::B_8 => (1 << 8) - 1, 526 | AdcPrecision::B_6 => (1 << 6) - 1, 527 | }, 528 | } 529 | } 530 | 531 | /// Read the value of a channel and converts the result to milli-volts 532 | pub fn read_abs_mv>(&mut self, pin: &mut PIN) -> u16 { 533 | let vdda = u32::from(VRef::read_vdda(self)); 534 | let v: u32 = self.read(pin).unwrap(); 535 | let max_samp = u32::from(self.max_sample()); 536 | 537 | (v * vdda / max_samp) as u16 538 | } 539 | 540 | fn calibrate(&mut self) { 541 | /* Ensure that ADEN = 0 */ 542 | if self.rb.cr.read().aden().is_enabled() { 543 | /* Clear ADEN by setting ADDIS */ 544 | self.rb.cr.modify(|_, w| w.addis().disable()); 545 | } 546 | while self.rb.cr.read().aden().is_enabled() {} 547 | 548 | /* Clear DMAEN */ 549 | self.rb.cfgr1.modify(|_, w| w.dmaen().disabled()); 550 | 551 | /* Start calibration by setting ADCAL */ 552 | self.rb.cr.modify(|_, w| w.adcal().start_calibration()); 553 | 554 | /* Wait until calibration is finished and ADCAL = 0 */ 555 | while self.rb.cr.read().adcal().is_calibrating() {} 556 | } 557 | 558 | fn select_clock(&mut self, rcc: &mut Rcc) { 559 | rcc.regs.apb2enr.modify(|_, w| w.adcen().enabled()); 560 | rcc.regs.cr2.modify(|_, w| w.hsi14on().on()); 561 | while rcc.regs.cr2.read().hsi14rdy().is_not_ready() {} 562 | } 563 | 564 | fn power_up(&mut self) { 565 | if self.rb.isr.read().adrdy().is_ready() { 566 | self.rb.isr.modify(|_, w| w.adrdy().clear()); 567 | } 568 | self.rb.cr.modify(|_, w| w.aden().enabled()); 569 | while self.rb.isr.read().adrdy().is_not_ready() {} 570 | } 571 | 572 | fn power_down(&mut self) { 573 | self.rb.cr.modify(|_, w| w.adstp().stop_conversion()); 574 | while self.rb.cr.read().adstp().is_stopping() {} 575 | self.rb.cr.modify(|_, w| w.addis().disable()); 576 | while self.rb.cr.read().aden().is_enabled() {} 577 | } 578 | 579 | fn convert(&mut self, chan: u8) -> u16 { 580 | self.rb.chselr.write(|w| unsafe { w.bits(1_u32 << chan) }); 581 | 582 | self.rb 583 | .smpr 584 | .write(|w| w.smp().variant(self.sample_time.into())); 585 | self.rb.cfgr1.modify(|_, w| { 586 | w.res() 587 | .variant(self.precision.into()) 588 | .align() 589 | .variant(self.align.into()) 590 | }); 591 | 592 | self.rb.cr.modify(|_, w| w.adstart().start_conversion()); 593 | while self.rb.isr.read().eoc().is_not_complete() {} 594 | 595 | let res = self.rb.dr.read().bits() as u16; 596 | if self.align == AdcAlign::Left && self.precision == AdcPrecision::B_6 { 597 | res << 8 598 | } else { 599 | res 600 | } 601 | } 602 | } 603 | 604 | impl OneShot for Adc 605 | where 606 | WORD: From, 607 | PIN: Channel, 608 | { 609 | type Error = (); 610 | 611 | fn read(&mut self, _pin: &mut PIN) -> nb::Result { 612 | self.power_up(); 613 | let res = self.convert(PIN::channel()); 614 | self.power_down(); 615 | Ok(res.into()) 616 | } 617 | } 618 | -------------------------------------------------------------------------------- /src/can.rs: -------------------------------------------------------------------------------- 1 | use bxcan::{FilterOwner, Instance, RegisterBlock}; 2 | 3 | use crate::gpio::gpioa::{PA11, PA12}; 4 | use crate::gpio::gpiob::{PB8, PB9}; 5 | use crate::gpio::{Alternate, AF4}; 6 | use crate::pac::CAN; 7 | use crate::rcc::Rcc; 8 | 9 | mod sealed { 10 | pub trait Sealed {} 11 | } 12 | 13 | use self::sealed::Sealed; 14 | 15 | pub use bxcan; 16 | 17 | pub trait RxPin: Sealed {} 18 | pub trait TxPin: Sealed {} 19 | 20 | macro_rules! can_pins { 21 | ( 22 | rx => [$($rx:ty),+ $(,)*], 23 | tx => [$($tx:ty),+ $(,)*], 24 | ) => { 25 | $( 26 | impl Sealed for $rx {} 27 | impl RxPin for $rx {} 28 | )+ 29 | $( 30 | impl Sealed for $tx {} 31 | impl TxPin for $tx {} 32 | )+ 33 | }; 34 | } 35 | 36 | #[cfg(any(feature = "stm32f042", feature = "stm32f072", feature = "stm32f091"))] 37 | can_pins! { 38 | rx => [PA11>, PB8>], 39 | tx => [PA12>, PB9>], 40 | } 41 | 42 | #[cfg(any(feature = "stm32f072", feature = "stm32f091"))] 43 | use crate::gpio::{ 44 | gpiod::{PD0, PD1}, 45 | AF0, 46 | }; 47 | 48 | #[cfg(any(feature = "stm32f072", feature = "stm32f091"))] 49 | can_pins! { 50 | rx => [PD0>], 51 | tx => [PD1>], 52 | } 53 | 54 | /// Resources used by the CAN peripheral. 55 | pub struct CanInstance { 56 | peripheral: CAN, 57 | tx: T, 58 | rx: R, 59 | } 60 | 61 | impl CanInstance { 62 | pub fn new(peripheral: CAN, tx: T, rx: R, rcc: &mut Rcc) -> Self { 63 | rcc.regs.apb1enr.modify(|_, w| w.canen().enabled()); 64 | rcc.regs.apb1rstr.modify(|_, w| w.canrst().reset()); 65 | rcc.regs.apb1rstr.modify(|_, w| w.canrst().clear_bit()); 66 | 67 | Self { peripheral, tx, rx } 68 | } 69 | 70 | pub fn into_raw(self) -> (CAN, T, R) { 71 | (self.peripheral, self.tx, self.rx) 72 | } 73 | 74 | /// Returns a reference to the raw CAN peripheral. 75 | pub unsafe fn peripheral(&mut self) -> &mut CAN { 76 | &mut self.peripheral 77 | } 78 | } 79 | 80 | unsafe impl Instance for CanInstance { 81 | const REGISTERS: *mut RegisterBlock = CAN::ptr() as *mut _; 82 | } 83 | 84 | unsafe impl FilterOwner for CanInstance { 85 | const NUM_FILTER_BANKS: u8 = 14; 86 | } 87 | -------------------------------------------------------------------------------- /src/dac.rs: -------------------------------------------------------------------------------- 1 | //! # API for the Digital to Analog converter 2 | //! 3 | //! Currently only supports writing to the DR of the DAC, 4 | //! just a basic one-shot conversion. 5 | //! 6 | //! ## Example 7 | //! ``` no_run 8 | //!#![deny(unused_imports)] 9 | //!#![no_main] 10 | //!#![no_std] 11 | //! 12 | //!use cortex_m; 13 | //!use cortex_m_rt as rt; 14 | //!use panic_halt; 15 | //! 16 | //!use stm32f0xx_hal as hal; 17 | //! 18 | //!use crate::hal::pac; 19 | //!use crate::hal::prelude::*; 20 | //!use crate::hal::dac::*; 21 | //! 22 | //!use rt::entry; 23 | //! 24 | //!enum Direction { 25 | //! Upcounting, 26 | //! Downcounting, 27 | //!} 28 | //! 29 | //!#[entry] 30 | //!fn main() -> ! { 31 | //! if let (Some(mut dp), Some(_cp)) = (pac::Peripherals::take(), cortex_m::Peripherals::take()) { 32 | //! cortex_m::interrupt::free(move |cs| { 33 | //! let mut rcc = dp.RCC.configure().sysclk(8.mhz()).freeze(&mut dp.FLASH); 34 | //! 35 | //! let gpioa = dp.GPIOA.split(&mut rcc); 36 | //! let mut dac = dac(dp.DAC, gpioa.pa4.into_analog(cs), &mut rcc); 37 | //! 38 | //! dac.enable(); 39 | //! 40 | //! let mut dir = Direction::Upcounting; 41 | //! let mut val = 0; 42 | //! 43 | //! dac.set_value(2058); 44 | //! cortex_m::asm::bkpt(); 45 | //! 46 | //! dac.set_value(4095); 47 | //! cortex_m::asm::bkpt(); 48 | //! 49 | //! loop { 50 | //! dac.set_value(val); 51 | //! match val { 52 | //! 0 => dir = Direction::Upcounting, 53 | //! 4095 => dir = Direction::Downcounting, 54 | //! _ => (), 55 | //! }; 56 | //! 57 | //! match dir { 58 | //! Direction::Upcounting => val += 1, 59 | //! Direction::Downcounting => val -= 1, 60 | //! } 61 | //! } 62 | //! }); 63 | //! } 64 | //! 65 | //! loop { 66 | //! continue; 67 | //! } 68 | //!} 69 | //! ``` 70 | #![deny(unused_imports)] 71 | use core::mem; 72 | 73 | use crate::gpio::gpioa::{PA4, PA5}; 74 | use crate::gpio::Analog; 75 | use crate::pac::DAC; 76 | use crate::rcc::Rcc; 77 | 78 | pub struct C1; 79 | pub struct C2; 80 | 81 | pub trait DacOut { 82 | fn set_value(&mut self, val: V); 83 | fn get_value(&mut self) -> V; 84 | } 85 | 86 | pub trait DacPin { 87 | fn enable(&mut self); 88 | } 89 | 90 | pub trait Pins { 91 | type Output; 92 | } 93 | 94 | impl Pins for PA4 { 95 | type Output = C1; 96 | } 97 | 98 | impl Pins for PA5 { 99 | type Output = C2; 100 | } 101 | 102 | impl Pins for (PA4, PA5) { 103 | type Output = (C1, C2); 104 | } 105 | 106 | pub fn dac(_dac: DAC, _pins: PINS, rcc: &mut Rcc) -> PINS::Output 107 | where 108 | PINS: Pins, 109 | { 110 | // Enable DAC clocks 111 | rcc.regs.apb1enr.modify(|_, w| w.dacen().set_bit()); 112 | 113 | // Reset DAC 114 | rcc.regs.apb1rstr.modify(|_, w| w.dacrst().set_bit()); 115 | rcc.regs.apb1rstr.modify(|_, w| w.dacrst().clear_bit()); 116 | 117 | unsafe { mem::MaybeUninit::uninit().assume_init() } 118 | } 119 | 120 | macro_rules! dac { 121 | ($CX:ident, $en:ident, $cen:ident, $cal_flag:ident, $trim:ident, $mode:ident, $dhrx:ident, $dac_dor:ident, $daccxdhr:ident) => { 122 | impl DacPin for $CX { 123 | fn enable(&mut self) { 124 | let dac = unsafe { &(*DAC::ptr()) }; 125 | dac.cr.modify(|_, w| w.$en().set_bit()); 126 | } 127 | } 128 | 129 | impl DacOut for $CX { 130 | fn set_value(&mut self, val: u16) { 131 | let dac = unsafe { &(*DAC::ptr()) }; 132 | dac.$dhrx.write(|w| unsafe { w.bits(val as u32) }); 133 | } 134 | 135 | fn get_value(&mut self) -> u16 { 136 | let dac = unsafe { &(*DAC::ptr()) }; 137 | dac.$dac_dor.read().bits() as u16 138 | } 139 | } 140 | }; 141 | } 142 | 143 | pub trait DacExt { 144 | fn constrain(self, pins: PINS, rcc: &mut Rcc) -> PINS::Output 145 | where 146 | PINS: Pins; 147 | } 148 | 149 | impl DacExt for DAC { 150 | fn constrain(self, pins: PINS, rcc: &mut Rcc) -> PINS::Output 151 | where 152 | PINS: Pins, 153 | { 154 | dac(self, pins, rcc) 155 | } 156 | } 157 | 158 | #[cfg(any( 159 | feature = "stm32f051", 160 | feature = "stm32f071", 161 | feature = "stm32f072", 162 | feature = "stm32f078", 163 | feature = "stm32f091", 164 | feature = "stm32f098", 165 | ))] 166 | dac!(C1, en1, cen1, cal_flag1, otrim1, mode1, dhr12r1, dor1, dacc1dhr); 167 | 168 | #[cfg(any( 169 | feature = "stm32f071", 170 | feature = "stm32f072", 171 | feature = "stm32f078", 172 | feature = "stm32f091", 173 | feature = "stm32f098", 174 | ))] 175 | dac!(C2, en2, cen2, cal_flag2, otrim2, mode2, dhr12r2, dor2, dacc2dhr); 176 | -------------------------------------------------------------------------------- /src/delay.rs: -------------------------------------------------------------------------------- 1 | //! API for delays with the systick timer 2 | //! 3 | //! Please be aware of potential overflows when using `delay_us`. 4 | //! E.g. at 48MHz the maximum delay is 89 seconds. 5 | //! 6 | //! Consider using the timers api as a more flexible interface 7 | //! 8 | //! # Example 9 | //! 10 | //! ``` no_run 11 | //! use stm32f0xx_hal as hal; 12 | //! 13 | //! use crate::hal::pac; 14 | //! use crate::hal::prelude::*; 15 | //! use crate::hal::delay::Delay; 16 | //! use cortex_m::peripheral::Peripherals; 17 | //! 18 | //! let mut p = pac::Peripherals::take().unwrap(); 19 | //! let mut cp = cortex_m::Peripherals::take().unwrap(); 20 | //! 21 | //! let mut rcc = p.RCC.configure().freeze(&mut p.FLASH); 22 | //! let mut delay = Delay::new(cp.SYST, &rcc); 23 | //! loop { 24 | //! delay.delay_ms(1_000_u16); 25 | //! } 26 | //! ``` 27 | 28 | use cast::{u16, u32}; 29 | use cortex_m::peripheral::syst::SystClkSource; 30 | use cortex_m::peripheral::SYST; 31 | 32 | use crate::rcc::Rcc; 33 | 34 | use embedded_hal::blocking::delay::{DelayMs, DelayUs}; 35 | 36 | /// System timer (SysTick) as a delay provider 37 | #[derive(Clone)] 38 | pub struct Delay { 39 | scale: u32, 40 | } 41 | 42 | const SYSTICK_RANGE: u32 = 0x0100_0000; 43 | 44 | impl Delay { 45 | /// Configures the system timer (SysTick) as a delay provider 46 | pub fn new(mut syst: SYST, rcc: &Rcc) -> Delay { 47 | syst.set_clock_source(SystClkSource::Core); 48 | 49 | syst.set_reload(SYSTICK_RANGE - 1); 50 | syst.clear_current(); 51 | syst.enable_counter(); 52 | 53 | assert!(rcc.clocks.hclk().0 >= 1_000_000); 54 | let scale = rcc.clocks.hclk().0 / 1_000_000; 55 | 56 | Delay { scale } 57 | // As access to the count register is possible without a reference to the systick, we can 58 | // just drop it 59 | } 60 | } 61 | 62 | impl DelayMs for Delay { 63 | // At 48 MHz (the maximum frequency), calling delay_us with ms * 1_000 directly overflows at 0x15D86 (just over the max u16 value) 64 | // So we implement a separate, higher level, delay loop 65 | fn delay_ms(&mut self, mut ms: u32) { 66 | const MAX_MS: u32 = 0x0000_FFFF; 67 | while ms != 0 { 68 | let current_ms = if ms <= MAX_MS { ms } else { MAX_MS }; 69 | self.delay_us(current_ms * 1_000); 70 | ms -= current_ms; 71 | } 72 | } 73 | } 74 | 75 | impl DelayMs for Delay { 76 | fn delay_ms(&mut self, ms: u16) { 77 | // Call delay_us directly, so we don't have to use the additional 78 | // delay loop the u32 variant uses 79 | self.delay_us(u32(ms) * 1_000); 80 | } 81 | } 82 | 83 | impl DelayMs for Delay { 84 | fn delay_ms(&mut self, ms: u8) { 85 | self.delay_ms(u16(ms)); 86 | } 87 | } 88 | 89 | // At 48MHz (the maximum frequency), this overflows at approx. 2^32 / 48 = 89 seconds 90 | impl DelayUs for Delay { 91 | fn delay_us(&mut self, us: u32) { 92 | // The SysTick Reload Value register supports values between 1 and 0x00FFFFFF. 93 | // Here less than maximum is used so we have some play if there's a long running interrupt. 94 | const MAX_TICKS: u32 = 0x007F_FFFF; 95 | 96 | let mut total_ticks = us * self.scale; 97 | 98 | while total_ticks != 0 { 99 | let current_ticks = if total_ticks <= MAX_TICKS { 100 | total_ticks 101 | } else { 102 | MAX_TICKS 103 | }; 104 | 105 | let start_count = SYST::get_current(); 106 | total_ticks -= current_ticks; 107 | 108 | // Use the wrapping substraction and the modulo to deal with the systick wrapping around 109 | // from 0 to 0xFFFF 110 | while (start_count.wrapping_sub(SYST::get_current()) % SYSTICK_RANGE) < current_ticks {} 111 | } 112 | } 113 | } 114 | 115 | impl DelayUs for Delay { 116 | fn delay_us(&mut self, us: u16) { 117 | self.delay_us(u32(us)) 118 | } 119 | } 120 | 121 | impl DelayUs for Delay { 122 | fn delay_us(&mut self, us: u8) { 123 | self.delay_us(u32(us)) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/flash.rs: -------------------------------------------------------------------------------- 1 | use core::convert::TryInto; 2 | use core::{mem, ptr, slice}; 3 | 4 | use embedded_storage::nor_flash::{ 5 | ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, 6 | }; 7 | 8 | use crate::pac::FLASH; 9 | use crate::signature::FlashSize; 10 | 11 | /// First address of the flash memory 12 | pub const FLASH_START: usize = 0x0800_0000; 13 | 14 | // F03x, F04x and F05x pages are 1K long 15 | #[cfg(any( 16 | feature = "stm32f030", 17 | feature = "stm32f031", 18 | feature = "stm32f038", 19 | feature = "stm32f042", 20 | feature = "stm32f048", 21 | feature = "stm32f051", 22 | feature = "stm32f058", 23 | ))] 24 | pub const PAGE_SIZE: u32 = 1024; 25 | // F03x, F04x and F05x have 64 flash pages 26 | #[cfg(any( 27 | feature = "stm32f030", 28 | feature = "stm32f031", 29 | feature = "stm32f038", 30 | feature = "stm32f042", 31 | feature = "stm32f048", 32 | feature = "stm32f051", 33 | feature = "stm32f058", 34 | ))] 35 | pub const NUM_PAGES: u32 = 64; 36 | 37 | // F07x and F09x pages are 2K long 38 | #[cfg(any( 39 | feature = "stm32f070", 40 | feature = "stm32f071", 41 | feature = "stm32f072", 42 | feature = "stm32f078", 43 | feature = "stm32f091", 44 | feature = "stm32f098", 45 | ))] 46 | pub const PAGE_SIZE: u32 = 2048; 47 | // F07x and F09x have 128 flash pages 48 | #[cfg(any( 49 | feature = "stm32f070", 50 | feature = "stm32f071", 51 | feature = "stm32f072", 52 | feature = "stm32f078", 53 | feature = "stm32f091", 54 | feature = "stm32f098", 55 | ))] 56 | pub const NUM_PAGES: u32 = 128; 57 | 58 | /// Flash erase/program error 59 | #[derive(Debug, Clone, Copy)] 60 | pub enum Error { 61 | Programming, 62 | WriteProtection, 63 | /// STM32F0 can only write Half Words (16 Bit) to flash. Can not write to addresses not aligned to that. 64 | Alignment, 65 | } 66 | 67 | impl Error { 68 | fn read(flash: &FLASH) -> Option { 69 | let sr = flash.sr.read(); 70 | if sr.pgerr().bit() { 71 | Some(Error::Programming) 72 | } else if sr.wrprt().bit() { 73 | Some(Error::WriteProtection) 74 | } else { 75 | None 76 | } 77 | } 78 | } 79 | 80 | /// Flash methods implemented for `pac::FLASH` 81 | #[allow(clippy::len_without_is_empty)] 82 | pub trait FlashExt { 83 | /// Memory-mapped address 84 | fn address(&self) -> usize; 85 | /// Size in bytes 86 | fn len(&self) -> usize; 87 | /// Returns a read-only view of flash memory 88 | fn read_all(&self) -> &[u8] { 89 | let ptr = self.address() as *const _; 90 | unsafe { slice::from_raw_parts(ptr, self.len()) } 91 | } 92 | /// Unlock flash for erasing/programming until this method's 93 | /// result is dropped 94 | fn unlocked(&mut self) -> UnlockedFlash; 95 | } 96 | 97 | impl FlashExt for FLASH { 98 | fn address(&self) -> usize { 99 | FLASH_START 100 | } 101 | 102 | fn len(&self) -> usize { 103 | FlashSize::get().bytes() 104 | } 105 | 106 | fn unlocked(&mut self) -> UnlockedFlash { 107 | unlock(self); 108 | UnlockedFlash { flash: self } 109 | } 110 | } 111 | 112 | /// Read-only flash 113 | /// 114 | /// # Examples 115 | /// 116 | /// ``` 117 | /// use stm32f0xx_hal::pac::Peripherals; 118 | /// use stm32f0xx_hal::flash::LockedFlash; 119 | /// use embedded_storage::nor_flash::ReadNorFlash; 120 | /// 121 | /// let dp = Peripherals::take().unwrap(); 122 | /// let mut flash = LockedFlash::new(dp.FLASH); 123 | /// println!("Flash capacity: {}", ReadNorFlash::capacity(&flash)); 124 | /// 125 | /// let mut buf = [0u8; 64]; 126 | /// ReadNorFlash::read(&mut flash, 0x0, &mut buf).unwrap(); 127 | /// println!("First 64 bytes of flash memory: {:?}", buf); 128 | /// ``` 129 | pub struct LockedFlash { 130 | flash: FLASH, 131 | } 132 | 133 | impl LockedFlash { 134 | pub fn new(flash: FLASH) -> Self { 135 | Self { flash } 136 | } 137 | } 138 | 139 | impl FlashExt for LockedFlash { 140 | fn address(&self) -> usize { 141 | self.flash.address() 142 | } 143 | 144 | fn len(&self) -> usize { 145 | self.flash.len() 146 | } 147 | 148 | fn unlocked(&mut self) -> UnlockedFlash { 149 | self.flash.unlocked() 150 | } 151 | } 152 | 153 | /// Result of `FlashExt::unlocked()` 154 | /// 155 | /// # Examples 156 | /// 157 | /// ``` 158 | /// use stm32f0xx_hal::pac::Peripherals; 159 | /// use stm32f0xx_hal::flash::{FlashExt, LockedFlash, UnlockedFlash}; 160 | /// use embedded_storage::nor_flash::NorFlash; 161 | /// 162 | /// let dp = Peripherals::take().unwrap(); 163 | /// let mut flash = LockedFlash::new(dp.FLASH); 164 | /// 165 | /// // Unlock flash for writing 166 | /// let mut unlocked_flash = flash.unlocked(); 167 | /// 168 | /// // Erase the second 128 KB sector. 169 | /// NorFlash::erase(&mut unlocked_flash, 128 * 1024, 256 * 1024).unwrap(); 170 | /// 171 | /// // Write some data at the start of the second 128 KB sector. 172 | /// let buf = [0u8; 64]; 173 | /// NorFlash::write(&mut unlocked_flash, 128 * 1024, &buf).unwrap(); 174 | /// 175 | /// // Lock flash by dropping 176 | /// drop(unlocked_flash); 177 | /// ``` 178 | pub struct UnlockedFlash<'a> { 179 | flash: &'a mut FLASH, 180 | } 181 | 182 | /// Automatically lock flash erase/program when leaving scope 183 | impl Drop for UnlockedFlash<'_> { 184 | fn drop(&mut self) { 185 | lock(self.flash); 186 | } 187 | } 188 | 189 | pub trait WriteErase { 190 | /// Native type for the flash for writing with the correct alignment and size 191 | /// 192 | /// Can be `u8`, `u16`, `u32`, ... (`u16` for STM32F0xx devices) 193 | type NativeType; 194 | 195 | /// The smallest possible write, depends on the platform 196 | fn program_native(&mut self, offset: usize, data: &[Self::NativeType]) -> Result<(), Error>; 197 | 198 | /// Write a buffer of bytes to memory and use native writes internally. 199 | /// If it is not the same length as a set of native writes the write will be padded to fill the 200 | /// native write. 201 | fn program(&mut self, offset: usize, data: &[u8]) -> Result<(), Error>; 202 | } 203 | 204 | impl WriteErase for UnlockedFlash<'_> { 205 | type NativeType = u16; 206 | 207 | fn program_native(&mut self, address: usize, data: &[Self::NativeType]) -> Result<(), Error> { 208 | // Wait for ready bit 209 | self.wait_ready(); 210 | 211 | let mut addr = address as *mut Self::NativeType; 212 | 213 | // Write the data to flash 214 | for &half_word in data { 215 | self.flash.cr.modify(|_, w| w.pg().set_bit()); 216 | unsafe { 217 | ptr::write_volatile(addr, half_word); 218 | addr = addr.add(1); 219 | } 220 | } 221 | 222 | self.wait_ready(); 223 | 224 | // Clear programming bit 225 | self.flash.cr.modify(|_, w| w.pg().clear_bit()); 226 | 227 | self.ok() 228 | } 229 | 230 | fn program(&mut self, mut address: usize, data: &[u8]) -> Result<(), Error> { 231 | if address % mem::align_of::() != 0 { 232 | return Err(Error::Alignment); 233 | } 234 | 235 | let mut chunks = data.chunks_exact(mem::size_of::()); 236 | 237 | for exact_chunk in &mut chunks { 238 | let native = &[Self::NativeType::from_ne_bytes( 239 | exact_chunk.try_into().unwrap(), 240 | )]; 241 | self.program_native(address, native)?; 242 | address += mem::size_of::(); 243 | } 244 | 245 | let remainder = chunks.remainder(); 246 | 247 | if !remainder.is_empty() { 248 | let mut data = Self::NativeType::MAX; 249 | 250 | for b in remainder.iter().rev() { 251 | data = (data << 8) | *b as Self::NativeType; 252 | } 253 | 254 | let native = &[data]; 255 | self.program_native(address, native)?; 256 | } 257 | 258 | self.ok() 259 | } 260 | } 261 | 262 | impl UnlockedFlash<'_> { 263 | /// Erase a flash page at offset 264 | /// 265 | /// Refer to the reference manual to see which sector corresponds 266 | /// to which memory address. 267 | pub fn erase(&mut self, offset: u32) -> Result<(), Error> { 268 | // Wait for ready bit 269 | self.wait_ready(); 270 | 271 | // Set the PER (page erase) bit in CR register 272 | self.flash.cr.modify(|_, w| w.per().set_bit()); 273 | 274 | // Write address into the AR register 275 | self.flash 276 | .ar 277 | .write(|w| w.far().bits(self.flash.address() as u32 + offset)); 278 | // Set the STRT (start) Bit in CR register 279 | self.flash.cr.modify(|_, w| w.strt().set_bit()); 280 | 281 | // Wait for the operation to finish 282 | self.wait_ready(); 283 | 284 | // Clear PER bit after operation is finished 285 | self.flash.cr.modify(|_, w| w.per().clear_bit()); 286 | self.ok() 287 | } 288 | 289 | fn ok(&self) -> Result<(), Error> { 290 | Error::read(self.flash).map(Err).unwrap_or(Ok(())) 291 | } 292 | 293 | fn wait_ready(&self) { 294 | while self.flash.sr.read().bsy().bit() {} 295 | } 296 | } 297 | 298 | const UNLOCK_KEY1: u32 = 0x45670123; 299 | const UNLOCK_KEY2: u32 = 0xCDEF89AB; 300 | 301 | #[allow(unused_unsafe)] 302 | fn unlock(flash: &FLASH) { 303 | flash.keyr.write(|w| unsafe { w.fkeyr().bits(UNLOCK_KEY1) }); 304 | flash.keyr.write(|w| unsafe { w.fkeyr().bits(UNLOCK_KEY2) }); 305 | assert!(!flash.cr.read().lock().bit()) 306 | } 307 | 308 | fn lock(flash: &FLASH) { 309 | flash.cr.modify(|_, w| w.lock().set_bit()); 310 | } 311 | 312 | /// Flash memory sector 313 | pub struct FlashSector { 314 | /// Sector number 315 | pub number: u8, 316 | /// Offset from base memory address 317 | pub offset: usize, 318 | /// Sector size in bytes 319 | pub size: usize, 320 | } 321 | 322 | impl FlashSector { 323 | /// Returns true if given offset belongs to this sector 324 | pub fn contains(&self, offset: usize) -> bool { 325 | self.offset <= offset && offset < self.offset + self.size 326 | } 327 | } 328 | 329 | /// Iterator of flash memory sectors in a single bank. 330 | /// Yields a size sequence of [16, 16, 16, 64, 128, 128, ..] 331 | pub struct FlashSectorIterator { 332 | index: u8, 333 | start_sector: u8, 334 | start_offset: usize, 335 | end_offset: usize, 336 | } 337 | 338 | impl FlashSectorIterator { 339 | fn new(start_sector: u8, start_offset: usize, end_offset: usize) -> Self { 340 | Self { 341 | index: 0, 342 | start_sector, 343 | start_offset, 344 | end_offset, 345 | } 346 | } 347 | } 348 | 349 | impl Iterator for FlashSectorIterator { 350 | type Item = FlashSector; 351 | 352 | fn next(&mut self) -> Option { 353 | if self.start_offset >= self.end_offset { 354 | None 355 | } else { 356 | // F03x, F04x and F05x sectors are 1K long 357 | #[cfg(any( 358 | feature = "stm32f030", 359 | feature = "stm32f031", 360 | feature = "stm32f038", 361 | feature = "stm32f042", 362 | feature = "stm32f048", 363 | feature = "stm32f051", 364 | feature = "stm32f058", 365 | ))] 366 | let size = 1024; 367 | 368 | // F07x and F09x sectors are 2K long 369 | #[cfg(any( 370 | feature = "stm32f070", 371 | feature = "stm32f071", 372 | feature = "stm32f072", 373 | feature = "stm32f078", 374 | feature = "stm32f091", 375 | feature = "stm32f098", 376 | ))] 377 | let size = 2048; 378 | 379 | let sector = FlashSector { 380 | number: self.start_sector + self.index, 381 | offset: self.start_offset, 382 | size, 383 | }; 384 | 385 | self.index += 1; 386 | self.start_offset += size; 387 | 388 | Some(sector) 389 | } 390 | } 391 | } 392 | 393 | /// Returns iterator of flash memory sectors for single and dual bank flash. 394 | /// Sectors are returned in continuous memory order, while sector numbers can have spaces between banks. 395 | pub fn flash_sectors(flash_size: usize) -> impl Iterator { 396 | // Chain an empty iterator to match types 397 | FlashSectorIterator::new(0, 0, flash_size).chain(FlashSectorIterator::new(0, 0, 0)) 398 | } 399 | 400 | impl NorFlashError for Error { 401 | fn kind(&self) -> NorFlashErrorKind { 402 | NorFlashErrorKind::Other 403 | } 404 | } 405 | 406 | impl ErrorType for LockedFlash { 407 | type Error = Error; 408 | } 409 | 410 | impl ErrorType for UnlockedFlash<'_> { 411 | type Error = Error; 412 | } 413 | 414 | impl ReadNorFlash for LockedFlash { 415 | const READ_SIZE: usize = 1; 416 | 417 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { 418 | let offset = offset as usize; 419 | bytes.copy_from_slice(&self.flash.read_all()[offset..offset + bytes.len()]); 420 | Ok(()) 421 | } 422 | 423 | fn capacity(&self) -> usize { 424 | self.flash.len() 425 | } 426 | } 427 | 428 | impl<'a> ReadNorFlash for UnlockedFlash<'a> { 429 | const READ_SIZE: usize = 1; 430 | 431 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { 432 | let offset = offset as usize; 433 | bytes.copy_from_slice(&self.flash.read_all()[offset..offset + bytes.len()]); 434 | Ok(()) 435 | } 436 | 437 | fn capacity(&self) -> usize { 438 | self.flash.len() 439 | } 440 | } 441 | 442 | impl<'a> NorFlash for UnlockedFlash<'a> { 443 | const WRITE_SIZE: usize = 2; 444 | 445 | const ERASE_SIZE: usize = PAGE_SIZE as usize; 446 | 447 | fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { 448 | let mut current = from as usize; 449 | 450 | for sector in flash_sectors(self.flash.len()) { 451 | if sector.contains(current) { 452 | UnlockedFlash::erase(self, current as u32)?; 453 | current += sector.size; 454 | } 455 | 456 | if current >= to as usize { 457 | break; 458 | } 459 | } 460 | 461 | Ok(()) 462 | } 463 | 464 | fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { 465 | self.program(self.flash.address() + offset as usize, bytes) 466 | } 467 | } 468 | -------------------------------------------------------------------------------- /src/i2c.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Deref; 2 | 3 | use embedded_hal::blocking::i2c::{Read, Write, WriteRead}; 4 | 5 | use crate::{ 6 | gpio::*, 7 | rcc::Rcc, 8 | time::{KiloHertz, U32Ext}, 9 | }; 10 | 11 | /// I2C abstraction 12 | pub struct I2c { 13 | i2c: I2C, 14 | pins: (SCLPIN, SDAPIN), 15 | } 16 | 17 | pub trait SclPin {} 18 | pub trait SdaPin {} 19 | 20 | macro_rules! i2c_pins { 21 | ($($I2C:ident => { 22 | scl => [$($scl:ty),+ $(,)*], 23 | sda => [$($sda:ty),+ $(,)*], 24 | })+) => { 25 | $( 26 | $( 27 | impl SclPin for $scl {} 28 | )+ 29 | $( 30 | impl SdaPin for $sda {} 31 | )+ 32 | )+ 33 | } 34 | } 35 | 36 | #[cfg(any( 37 | feature = "stm32f030", 38 | feature = "stm32f031", 39 | feature = "stm32f038", 40 | feature = "stm32f042", 41 | feature = "stm32f048", 42 | feature = "stm32f051", 43 | feature = "stm32f058", 44 | feature = "stm32f070", 45 | feature = "stm32f071", 46 | feature = "stm32f072", 47 | feature = "stm32f078", 48 | feature = "stm32f091", 49 | feature = "stm32f098", 50 | ))] 51 | i2c_pins! { 52 | I2C1 => { 53 | scl => [gpiob::PB6>, gpiob::PB8>], 54 | sda => [gpiob::PB7>, gpiob::PB9>], 55 | } 56 | } 57 | #[cfg(any( 58 | feature = "stm32f030x4", 59 | feature = "stm32f030x6", 60 | feature = "stm32f030xc", 61 | feature = "stm32f031", 62 | feature = "stm32f038", 63 | feature = "stm32f042", 64 | feature = "stm32f048", 65 | feature = "stm32f070x6", 66 | feature = "stm32f091", 67 | feature = "stm32f098", 68 | ))] 69 | i2c_pins! { 70 | I2C1 => { 71 | scl => [gpioa::PA9>], 72 | sda => [gpioa::PA10>], 73 | } 74 | } 75 | #[cfg(any(feature = "stm32f042", feature = "stm32f048"))] 76 | i2c_pins! { 77 | I2C1 => { 78 | scl => [gpioa::PA11>], 79 | sda => [gpioa::PA12>], 80 | } 81 | } 82 | #[cfg(any( 83 | feature = "stm32f030x4", 84 | feature = "stm32f030x6", 85 | feature = "stm32f031", 86 | feature = "stm32f038", 87 | feature = "stm32f042", 88 | feature = "stm32f048", 89 | ))] 90 | i2c_pins! { 91 | I2C1 => { 92 | scl => [gpiob::PB10>], 93 | sda => [gpiob::PB11>], 94 | } 95 | } 96 | #[cfg(any(feature = "stm32f030xc", feature = "stm32f042", feature = "stm32f048"))] 97 | i2c_pins! { 98 | I2C1 => { 99 | scl => [gpiob::PB13>], 100 | sda => [gpiob::PB14>], 101 | } 102 | } 103 | #[cfg(any( 104 | feature = "stm32f030xc", 105 | feature = "stm32f042", 106 | feature = "stm32f048", 107 | feature = "stm32f070x6", 108 | feature = "stm32f091", 109 | feature = "stm32f098", 110 | ))] 111 | i2c_pins! { 112 | I2C1 => { 113 | scl => [gpiof::PF1>], 114 | sda => [gpiof::PF0>], 115 | } 116 | } 117 | 118 | #[cfg(any(feature = "stm32f030x8", feature = "stm32f051", feature = "stm32f058"))] 119 | i2c_pins! { 120 | I2C2 => { 121 | scl => [gpiob::PB10>], 122 | sda => [gpiob::PB11>], 123 | } 124 | } 125 | #[cfg(any( 126 | feature = "stm32f030xc", 127 | feature = "stm32f070xb", 128 | feature = "stm32f071", 129 | feature = "stm32f072", 130 | feature = "stm32f078", 131 | feature = "stm32f091", 132 | feature = "stm32f098", 133 | ))] 134 | i2c_pins! { 135 | I2C2 => { 136 | scl => [gpiob::PB10>, gpiob::PB13>], 137 | sda => [gpiob::PB11>, gpiob::PB14>], 138 | } 139 | } 140 | #[cfg(any(feature = "stm32f091", feature = "stm32f098"))] 141 | i2c_pins! { 142 | I2C2 => { 143 | scl => [gpioa::PA11>], 144 | sda => [gpioa::PA12>], 145 | } 146 | } 147 | 148 | #[derive(Debug)] 149 | pub enum Error { 150 | OVERRUN, 151 | NACK, 152 | BUS, 153 | } 154 | 155 | macro_rules! i2c { 156 | ($($I2C:ident: ($i2c:ident, $i2cXen:ident, $i2cXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => { 157 | $( 158 | use crate::pac::$I2C; 159 | impl I2c<$I2C, SCLPIN, SDAPIN> { 160 | pub fn $i2c(i2c: $I2C, pins: (SCLPIN, SDAPIN), speed: KiloHertz, rcc: &mut Rcc) -> Self 161 | where 162 | SCLPIN: SclPin<$I2C>, 163 | SDAPIN: SdaPin<$I2C>, 164 | { 165 | // Enable clock for I2C 166 | rcc.regs.$apbenr.modify(|_, w| w.$i2cXen().set_bit()); 167 | 168 | // Reset I2C 169 | rcc.regs.$apbrstr.modify(|_, w| w.$i2cXrst().set_bit()); 170 | rcc.regs.$apbrstr.modify(|_, w| w.$i2cXrst().clear_bit()); 171 | I2c { i2c, pins }.i2c_init(speed) 172 | } 173 | } 174 | )+ 175 | } 176 | } 177 | 178 | i2c! { 179 | I2C1: (i2c1, i2c1en, i2c1rst, apb1enr, apb1rstr), 180 | } 181 | 182 | #[cfg(any( 183 | feature = "stm32f030x8", 184 | feature = "stm32f030xc", 185 | feature = "stm32f051", 186 | feature = "stm32f058", 187 | feature = "stm32f070xb", 188 | feature = "stm32f071", 189 | feature = "stm32f072", 190 | feature = "stm32f078", 191 | feature = "stm32f091", 192 | feature = "stm32f098", 193 | ))] 194 | i2c! { 195 | I2C2: (i2c2, i2c2en, i2c2rst, apb1enr, apb1rstr), 196 | } 197 | 198 | // It's s needed for the impls, but rustc doesn't recognize that 199 | #[allow(dead_code)] 200 | type I2cRegisterBlock = crate::pac::i2c1::RegisterBlock; 201 | 202 | impl I2c 203 | where 204 | I2C: Deref, 205 | { 206 | fn i2c_init(self, speed: KiloHertz) -> Self { 207 | use core::cmp; 208 | 209 | // Make sure the I2C unit is disabled so we can configure it 210 | self.i2c.cr1.modify(|_, w| w.pe().clear_bit()); 211 | 212 | // Calculate settings for I2C speed modes 213 | let presc; 214 | let scldel; 215 | let sdadel; 216 | let sclh; 217 | let scll; 218 | 219 | // We're using HSI here which runs at a fixed 8MHz 220 | const FREQ: u32 = 8_000_000; 221 | 222 | // Normal I2C speeds use a different scaling than fast mode below 223 | if speed <= 100_u32.khz() { 224 | presc = 1; 225 | scll = cmp::max((((FREQ >> presc) >> 1) / speed.0) - 1, 255) as u8; 226 | sclh = scll - 4; 227 | sdadel = 2; 228 | scldel = 4; 229 | } else { 230 | presc = 0; 231 | scll = cmp::max((((FREQ >> presc) >> 1) / speed.0) - 1, 255) as u8; 232 | sclh = scll - 6; 233 | sdadel = 1; 234 | scldel = 3; 235 | } 236 | 237 | // Enable I2C signal generator, and configure I2C for configured speed 238 | self.i2c.timingr.write(|w| { 239 | w.presc() 240 | .bits(presc) 241 | .scldel() 242 | .bits(scldel) 243 | .sdadel() 244 | .bits(sdadel) 245 | .sclh() 246 | .bits(sclh) 247 | .scll() 248 | .bits(scll) 249 | }); 250 | 251 | // Enable the I2C processing 252 | self.i2c.cr1.modify(|_, w| w.pe().set_bit()); 253 | 254 | self 255 | } 256 | 257 | pub fn release(self) -> (I2C, (SCLPIN, SDAPIN)) { 258 | (self.i2c, self.pins) 259 | } 260 | 261 | fn check_and_clear_error_flags(&self, isr: &crate::stm32::i2c1::isr::R) -> Result<(), Error> { 262 | // If we have a set overrun flag, clear it and return an OVERRUN error 263 | if isr.ovr().bit_is_set() { 264 | self.i2c.icr.write(|w| w.ovrcf().set_bit()); 265 | return Err(Error::OVERRUN); 266 | } 267 | 268 | // If we have a set arbitration error or bus error flag, clear it and return an BUS error 269 | if isr.arlo().bit_is_set() | isr.berr().bit_is_set() { 270 | self.i2c 271 | .icr 272 | .write(|w| w.arlocf().set_bit().berrcf().set_bit()); 273 | return Err(Error::BUS); 274 | } 275 | 276 | // If we received a NACK, then signal as a NACK error 277 | if isr.nackf().bit_is_set() { 278 | self.i2c 279 | .icr 280 | .write(|w| w.stopcf().set_bit().nackcf().set_bit()); 281 | return Err(Error::NACK); 282 | } 283 | 284 | Ok(()) 285 | } 286 | 287 | fn send_byte(&self, byte: u8) -> Result<(), Error> { 288 | // Wait until we're ready for sending 289 | loop { 290 | let isr = self.i2c.isr.read(); 291 | self.check_and_clear_error_flags(&isr)?; 292 | if isr.txis().bit_is_set() { 293 | break; 294 | } 295 | } 296 | 297 | // Push out a byte of data 298 | self.i2c.txdr.write(|w| unsafe { w.bits(u32::from(byte)) }); 299 | 300 | self.check_and_clear_error_flags(&self.i2c.isr.read())?; 301 | Ok(()) 302 | } 303 | 304 | fn recv_byte(&self) -> Result { 305 | loop { 306 | let isr = self.i2c.isr.read(); 307 | self.check_and_clear_error_flags(&isr)?; 308 | if isr.rxne().bit_is_set() { 309 | break; 310 | } 311 | } 312 | 313 | let value = self.i2c.rxdr.read().bits() as u8; 314 | Ok(value) 315 | } 316 | } 317 | 318 | impl WriteRead for I2c 319 | where 320 | I2C: Deref, 321 | { 322 | type Error = Error; 323 | 324 | fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { 325 | // Set up current slave address for writing and disable autoending 326 | self.i2c.cr2.modify(|_, w| { 327 | w.sadd() 328 | .bits(u16::from(addr) << 1) 329 | .nbytes() 330 | .bits(bytes.len() as u8) 331 | .rd_wrn() 332 | .clear_bit() 333 | .autoend() 334 | .clear_bit() 335 | }); 336 | 337 | // Send a START condition 338 | self.i2c.cr2.modify(|_, w| w.start().set_bit()); 339 | 340 | // Wait until the transmit buffer is empty and there hasn't been any error condition 341 | loop { 342 | let isr = self.i2c.isr.read(); 343 | self.check_and_clear_error_flags(&isr)?; 344 | if isr.txis().bit_is_set() || isr.tc().bit_is_set() { 345 | break; 346 | } 347 | } 348 | 349 | // Send out all individual bytes 350 | for c in bytes { 351 | self.send_byte(*c)?; 352 | } 353 | 354 | // Wait until data was sent 355 | loop { 356 | let isr = self.i2c.isr.read(); 357 | self.check_and_clear_error_flags(&isr)?; 358 | if isr.tc().bit_is_set() { 359 | break; 360 | } 361 | } 362 | 363 | // Set up current address for reading 364 | self.i2c.cr2.modify(|_, w| { 365 | w.sadd() 366 | .bits(u16::from(addr) << 1) 367 | .nbytes() 368 | .bits(buffer.len() as u8) 369 | .rd_wrn() 370 | .set_bit() 371 | }); 372 | 373 | // Send another START condition 374 | self.i2c.cr2.modify(|_, w| w.start().set_bit()); 375 | 376 | // Send the autoend after setting the start to get a restart 377 | self.i2c.cr2.modify(|_, w| w.autoend().set_bit()); 378 | 379 | // Now read in all bytes 380 | for c in buffer.iter_mut() { 381 | *c = self.recv_byte()?; 382 | } 383 | 384 | // Check and clear flags if they somehow ended up set 385 | self.check_and_clear_error_flags(&self.i2c.isr.read())?; 386 | 387 | Ok(()) 388 | } 389 | } 390 | 391 | impl Read for I2c 392 | where 393 | I2C: Deref, 394 | { 395 | type Error = Error; 396 | 397 | fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { 398 | // Set up current address for reading 399 | self.i2c.cr2.modify(|_, w| { 400 | w.sadd() 401 | .bits(u16::from(addr) << 1) 402 | .nbytes() 403 | .bits(buffer.len() as u8) 404 | .rd_wrn() 405 | .set_bit() 406 | }); 407 | 408 | // Send a START condition 409 | self.i2c.cr2.modify(|_, w| w.start().set_bit()); 410 | 411 | // Send the autoend after setting the start to get a restart 412 | self.i2c.cr2.modify(|_, w| w.autoend().set_bit()); 413 | 414 | // Now read in all bytes 415 | for c in buffer.iter_mut() { 416 | *c = self.recv_byte()?; 417 | } 418 | 419 | // Check and clear flags if they somehow ended up set 420 | self.check_and_clear_error_flags(&self.i2c.isr.read())?; 421 | 422 | Ok(()) 423 | } 424 | } 425 | 426 | impl Write for I2c 427 | where 428 | I2C: Deref, 429 | { 430 | type Error = Error; 431 | 432 | fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { 433 | // Set up current slave address for writing and enable autoending 434 | self.i2c.cr2.modify(|_, w| { 435 | w.sadd() 436 | .bits(u16::from(addr) << 1) 437 | .nbytes() 438 | .bits(bytes.len() as u8) 439 | .rd_wrn() 440 | .clear_bit() 441 | .autoend() 442 | .set_bit() 443 | }); 444 | 445 | // Send a START condition 446 | self.i2c.cr2.modify(|_, w| w.start().set_bit()); 447 | 448 | // Send out all individual bytes 449 | for c in bytes { 450 | self.send_byte(*c)?; 451 | } 452 | 453 | // Check and clear flags if they somehow ended up set 454 | self.check_and_clear_error_flags(&self.i2c.isr.read())?; 455 | 456 | Ok(()) 457 | } 458 | } 459 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![allow(non_camel_case_types)] 3 | #![allow(clippy::uninit_assumed_init)] 4 | 5 | pub use stm32f0; 6 | 7 | #[cfg(any(feature = "stm32f030", feature = "stm32f070"))] 8 | pub use stm32f0::stm32f0x0 as pac; 9 | 10 | #[cfg(any( 11 | feature = "stm32f031", 12 | feature = "stm32f051", 13 | feature = "stm32f071", 14 | feature = "stm32f091", 15 | ))] 16 | pub use stm32f0::stm32f0x1 as pac; 17 | 18 | #[cfg(any(feature = "stm32f042", feature = "stm32f072"))] 19 | pub use stm32f0::stm32f0x2 as pac; 20 | 21 | #[cfg(any( 22 | feature = "stm32f038", 23 | feature = "stm32f048", 24 | feature = "stm32f058", 25 | feature = "stm32f078", 26 | feature = "stm32f098", 27 | ))] 28 | pub use stm32f0::stm32f0x8 as pac; 29 | 30 | #[cfg(feature = "device-selected")] 31 | pub mod adc; 32 | #[cfg(any( 33 | feature = "stm32f051", 34 | feature = "stm32f071", 35 | feature = "stm32f072", 36 | feature = "stm32f078", 37 | feature = "stm32f091", 38 | feature = "stm32f098", 39 | ))] 40 | pub mod dac; 41 | #[cfg(feature = "device-selected")] 42 | pub mod delay; 43 | #[cfg(feature = "device-selected")] 44 | pub mod flash; 45 | #[cfg(feature = "device-selected")] 46 | pub mod gpio; 47 | #[cfg(feature = "device-selected")] 48 | pub mod i2c; 49 | #[cfg(feature = "device-selected")] 50 | pub mod prelude; 51 | #[cfg(feature = "device-selected")] 52 | pub mod pwm; 53 | #[cfg(feature = "device-selected")] 54 | pub mod rcc; 55 | #[cfg(feature = "device-selected")] 56 | pub mod serial; 57 | #[cfg(feature = "device-selected")] 58 | pub mod signature; 59 | #[cfg(feature = "device-selected")] 60 | pub mod spi; 61 | #[cfg(feature = "device-selected")] 62 | pub mod time; 63 | #[cfg(feature = "device-selected")] 64 | pub mod timers; 65 | #[cfg(any( 66 | feature = "stm32f031", 67 | feature = "stm32f051", 68 | feature = "stm32f071", 69 | feature = "stm32f091", 70 | feature = "stm32f042", 71 | feature = "stm32f072", 72 | feature = "stm32f038", 73 | feature = "stm32f048", 74 | feature = "stm32f058", 75 | feature = "stm32f078", 76 | feature = "stm32f098", 77 | ))] 78 | pub mod tsc; 79 | #[cfg(all( 80 | feature = "stm32-usbd", 81 | any( 82 | feature = "stm32f042", 83 | feature = "stm32f048", 84 | feature = "stm32f072", 85 | feature = "stm32f078", 86 | feature = "stm32f070x6", 87 | feature = "stm32f070xb", 88 | ) 89 | ))] 90 | pub mod usb; 91 | #[cfg(feature = "device-selected")] 92 | pub mod watchdog; 93 | 94 | #[cfg(all( 95 | feature = "device-selected", 96 | any(feature = "stm32f091", feature = "stm32f042", feature = "stm32f072",) 97 | ))] 98 | pub mod can; 99 | 100 | #[cfg(feature = "device-selected")] 101 | #[deprecated(since = "0.17.0", note = "please use `pac` instead")] 102 | pub use pac as stm32; 103 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use embedded_hal::prelude::*; 2 | 3 | // TODO for some reason, watchdog isn't in the embedded_hal prelude 4 | pub use embedded_hal::watchdog::Watchdog as _stm32f0xx_hal_embedded_hal_watchdog_Watchdog; 5 | pub use embedded_hal::watchdog::WatchdogEnable as _stm32f0xx_hal_embedded_hal_watchdog_WatchdogEnable; 6 | 7 | pub use embedded_hal::adc::OneShot as _embedded_hal_adc_OneShot; 8 | 9 | pub use embedded_hal::digital::v2::InputPin as _embedded_hal_gpio_InputPin; 10 | pub use embedded_hal::digital::v2::OutputPin as _embedded_hal_gpio_OutputPin; 11 | pub use embedded_hal::digital::v2::StatefulOutputPin as _embedded_hal_gpio_StatefulOutputPin; 12 | pub use embedded_hal::digital::v2::ToggleableOutputPin as _embedded_hal_gpio_ToggleableOutputPin; 13 | 14 | pub use crate::gpio::GpioExt as _stm32f0xx_hal_gpio_GpioExt; 15 | pub use crate::rcc::RccExt as _stm32f0xx_hal_rcc_RccExt; 16 | pub use crate::time::U32Ext as _stm32f0xx_hal_time_U32Ext; 17 | -------------------------------------------------------------------------------- /src/serial.rs: -------------------------------------------------------------------------------- 1 | //! API for the integrated USART ports 2 | //! 3 | //! This only implements the usual asynchronous bidirectional 8-bit transfers. 4 | //! 5 | //! It's possible to use a read-only/write-only serial implementation with 6 | //! `usartXrx`/`usartXtx`. 7 | //! 8 | //! # Examples 9 | //! Echo 10 | //! ``` no_run 11 | //! use stm32f0xx_hal as hal; 12 | //! 13 | //! use crate::hal::prelude::*; 14 | //! use crate::hal::serial::Serial; 15 | //! use crate::hal::pac; 16 | //! 17 | //! use nb::block; 18 | //! 19 | //! cortex_m::interrupt::free(|cs| { 20 | //! let rcc = p.RCC.configure().sysclk(48.mhz()).freeze(); 21 | //! 22 | //! let gpioa = p.GPIOA.split(&mut rcc); 23 | //! 24 | //! let tx = gpioa.pa9.into_alternate_af1(cs); 25 | //! let rx = gpioa.pa10.into_alternate_af1(cs); 26 | //! 27 | //! let mut serial = Serial::usart1(p.USART1, (tx, rx), 115_200.bps(), &mut rcc); 28 | //! 29 | //! loop { 30 | //! let received = block!(serial.read()).unwrap(); 31 | //! block!(serial.write(received)).ok(); 32 | //! } 33 | //! }); 34 | //! ``` 35 | //! 36 | //! Hello World 37 | //! ``` no_run 38 | //! use stm32f0xx_hal as hal; 39 | //! 40 | //! use crate::hal::prelude::*; 41 | //! use crate::hal::serial::Serial; 42 | //! use crate::hal::pac; 43 | //! 44 | //! use nb::block; 45 | //! 46 | //! cortex_m::interrupt::free(|cs| { 47 | //! let rcc = p.RCC.configure().sysclk(48.mhz()).freeze(); 48 | //! 49 | //! let gpioa = p.GPIOA.split(&mut rcc); 50 | //! 51 | //! let tx = gpioa.pa9.into_alternate_af1(cs); 52 | //! 53 | //! let mut serial = Serial::usart1tx(p.USART1, tx, 115_200.bps(), &mut rcc); 54 | //! 55 | //! loop { 56 | //! serial.write_str("Hello World!\r\n"); 57 | //! } 58 | //! }); 59 | //! ``` 60 | 61 | use core::{ 62 | convert::Infallible, 63 | fmt::{Result, Write}, 64 | ops::Deref, 65 | }; 66 | 67 | use embedded_hal::prelude::*; 68 | 69 | use crate::{gpio::*, rcc::Rcc, time::Bps}; 70 | 71 | use core::marker::PhantomData; 72 | 73 | /// Serial error 74 | #[non_exhaustive] 75 | #[derive(Debug)] 76 | pub enum Error { 77 | /// Framing error 78 | Framing, 79 | /// Noise error 80 | Noise, 81 | /// RX buffer overrun 82 | Overrun, 83 | /// Parity check error 84 | Parity, 85 | } 86 | 87 | /// Interrupt event 88 | pub enum Event { 89 | /// New data has been received 90 | Rxne, 91 | /// New data can be sent 92 | Txe, 93 | /// Idle line state detected 94 | Idle, 95 | } 96 | 97 | pub trait TxPin {} 98 | pub trait RxPin {} 99 | 100 | macro_rules! usart_pins { 101 | ($($USART:ident => { 102 | tx => [$($tx:ty),+ $(,)*], 103 | rx => [$($rx:ty),+ $(,)*], 104 | })+) => { 105 | $( 106 | $( 107 | impl TxPin for $tx {} 108 | )+ 109 | $( 110 | impl RxPin for $rx {} 111 | )+ 112 | )+ 113 | } 114 | } 115 | 116 | #[cfg(any( 117 | feature = "stm32f030", 118 | feature = "stm32f031", 119 | feature = "stm32f038", 120 | feature = "stm32f042", 121 | feature = "stm32f048", 122 | feature = "stm32f051", 123 | feature = "stm32f058", 124 | feature = "stm32f070", 125 | feature = "stm32f071", 126 | feature = "stm32f072", 127 | feature = "stm32f078", 128 | feature = "stm32f091", 129 | feature = "stm32f098", 130 | ))] 131 | usart_pins! { 132 | USART1 => { 133 | tx => [gpioa::PA9>, gpiob::PB6>], 134 | rx => [gpioa::PA10>, gpiob::PB7>], 135 | } 136 | } 137 | #[cfg(any( 138 | feature = "stm32f030x4", 139 | feature = "stm32f030x6", 140 | feature = "stm32f031", 141 | feature = "stm32f038", 142 | ))] 143 | usart_pins! { 144 | USART1 => { 145 | tx => [gpioa::PA2>, gpioa::PA14>], 146 | rx => [gpioa::PA3>, gpioa::PA15>], 147 | } 148 | } 149 | 150 | #[cfg(any( 151 | feature = "stm32f030x8", 152 | feature = "stm32f030xc", 153 | feature = "stm32f042", 154 | feature = "stm32f048", 155 | feature = "stm32f051", 156 | feature = "stm32f058", 157 | feature = "stm32f070", 158 | feature = "stm32f071", 159 | feature = "stm32f072", 160 | feature = "stm32f078", 161 | feature = "stm32f091", 162 | feature = "stm32f098", 163 | ))] 164 | usart_pins! { 165 | USART2 => { 166 | tx => [gpioa::PA2>, gpioa::PA14>], 167 | rx => [gpioa::PA3>, gpioa::PA15>], 168 | } 169 | } 170 | #[cfg(any( 171 | feature = "stm32f071", 172 | feature = "stm32f072", 173 | feature = "stm32f078", 174 | feature = "stm32f091", 175 | feature = "stm32f098", 176 | ))] 177 | usart_pins! { 178 | USART2 => { 179 | tx => [gpiod::PD5>], 180 | rx => [gpiod::PD6>], 181 | } 182 | } 183 | 184 | #[cfg(any( 185 | feature = "stm32f030xc", 186 | feature = "stm32f070xb", 187 | feature = "stm32f071", 188 | feature = "stm32f072", 189 | feature = "stm32f078", 190 | feature = "stm32f091", 191 | feature = "stm32f098", 192 | ))] 193 | usart_pins! { 194 | USART3 => { 195 | // According to the datasheet PB10 is both tx and rx, but in stm32cubemx it's only tx 196 | tx => [gpiob::PB10>, gpioc::PC4>, gpioc::PC10>], 197 | rx => [gpiob::PB11>, gpioc::PC5>, gpioc::PC11>], 198 | } 199 | USART4 => { 200 | tx => [gpioa::PA0>, gpioc::PC10>], 201 | rx => [gpioa::PA1>, gpioc::PC11>], 202 | } 203 | } 204 | #[cfg(any( 205 | feature = "stm32f071", 206 | feature = "stm32f072", 207 | feature = "stm32f078", 208 | feature = "stm32f091", 209 | feature = "stm32f098", 210 | ))] 211 | usart_pins! { 212 | USART3 => { 213 | tx => [gpiod::PD8>], 214 | rx => [gpiod::PD9>], 215 | } 216 | } 217 | // TODO: The ST SVD files are missing the entire PE enable register. 218 | // Re-enable as soon as this gets fixed. 219 | // #[cfg(any(feature = "stm32f091", feature = "stm32f098"))] 220 | // usart_pins! { 221 | // USART4 => { 222 | // tx => [gpioe::PE8>], 223 | // rx => [gpioe::PE9>], 224 | // } 225 | // } 226 | 227 | #[cfg(any(feature = "stm32f030xc", feature = "stm32f091", feature = "stm32f098"))] 228 | usart_pins! { 229 | USART5 => { 230 | tx => [gpioc::PC12>], 231 | rx => [gpiod::PD2>], 232 | } 233 | USART6 => { 234 | tx => [gpioa::PA4>, gpioc::PC0>], 235 | rx => [gpioa::PA5>, gpioc::PC1>], 236 | } 237 | } 238 | #[cfg(any(feature = "stm32f030xc", feature = "stm32f091"))] 239 | usart_pins! { 240 | USART5 => { 241 | tx => [gpiob::PB3>], 242 | rx => [gpiob::PB4>], 243 | } 244 | } 245 | // TODO: The ST SVD files are missing the entire PE enable register. 246 | // Re-enable as soon as this gets fixed. 247 | #[cfg(any(feature = "stm32f091", feature = "stm32f098"))] 248 | usart_pins! { 249 | // USART5 => { 250 | // tx => [gpioe::PE10>], 251 | // rx => [gpioe::PE11>], 252 | // } 253 | USART6 => { 254 | tx => [gpiof::PF9>], 255 | rx => [gpiof::PF10>], 256 | } 257 | } 258 | 259 | /// Serial abstraction 260 | pub struct Serial { 261 | usart: USART, 262 | pins: (TXPIN, RXPIN), 263 | } 264 | 265 | // Common register 266 | type SerialRegisterBlock = crate::pac::usart1::RegisterBlock; 267 | 268 | /// Serial receiver 269 | pub struct Rx { 270 | usart: *const SerialRegisterBlock, 271 | _instance: PhantomData, 272 | } 273 | 274 | // NOTE(unsafe) Required to allow protected shared access in handlers 275 | unsafe impl Send for Rx {} 276 | 277 | /// Serial transmitter 278 | pub struct Tx { 279 | usart: *const SerialRegisterBlock, 280 | _instance: PhantomData, 281 | } 282 | 283 | // NOTE(unsafe) Required to allow protected shared access in handlers 284 | unsafe impl Send for Tx {} 285 | 286 | macro_rules! usart { 287 | ($($USART:ident: ($usart:ident, $usarttx:ident, $usartrx:ident, $usartXen:ident, $apbenr:ident),)+) => { 288 | $( 289 | use crate::pac::$USART; 290 | impl Serial<$USART, TXPIN, RXPIN> 291 | where 292 | TXPIN: TxPin<$USART>, 293 | RXPIN: RxPin<$USART>, 294 | { 295 | /// Creates a new serial instance 296 | pub fn $usart(usart: $USART, pins: (TXPIN, RXPIN), baud_rate: Bps, rcc: &mut Rcc) -> Self 297 | { 298 | let mut serial = Serial { usart, pins }; 299 | serial.configure(baud_rate, rcc); 300 | // Enable transmission and receiving 301 | serial.usart.cr1.modify(|_, w| w.te().set_bit().re().set_bit().ue().set_bit()); 302 | serial 303 | } 304 | } 305 | 306 | impl Serial<$USART, TXPIN, ()> 307 | where 308 | TXPIN: TxPin<$USART>, 309 | { 310 | /// Creates a new tx-only serial instance 311 | pub fn $usarttx(usart: $USART, txpin: TXPIN, baud_rate: Bps, rcc: &mut Rcc) -> Self 312 | { 313 | let rxpin = (); 314 | let mut serial = Serial { usart, pins: (txpin, rxpin) }; 315 | serial.configure(baud_rate, rcc); 316 | // Enable transmission 317 | serial.usart.cr1.modify(|_, w| w.te().set_bit().ue().set_bit()); 318 | serial 319 | } 320 | } 321 | 322 | impl Serial<$USART, (), RXPIN> 323 | where 324 | RXPIN: RxPin<$USART>, 325 | { 326 | /// Creates a new rx-only serial instance 327 | pub fn $usartrx(usart: $USART, rxpin: RXPIN, baud_rate: Bps, rcc: &mut Rcc) -> Self 328 | { 329 | let txpin = (); 330 | let mut serial = Serial { usart, pins: (txpin, rxpin) }; 331 | serial.configure(baud_rate, rcc); 332 | // Enable receiving 333 | serial.usart.cr1.modify(|_, w| w.re().set_bit().ue().set_bit()); 334 | serial 335 | } 336 | } 337 | 338 | impl Serial<$USART, TXPIN, RXPIN> { 339 | fn configure(&mut self, baud_rate: Bps, rcc: &mut Rcc) { 340 | // Enable clock for USART 341 | rcc.regs.$apbenr.modify(|_, w| w.$usartXen().set_bit()); 342 | 343 | // Calculate correct baudrate divisor on the fly 344 | let brr = rcc.clocks.pclk().0 / baud_rate.0; 345 | self.usart.brr.write(|w| unsafe { w.bits(brr) }); 346 | 347 | // Reset other registers to disable advanced USART features 348 | self.usart.cr2.reset(); 349 | self.usart.cr3.reset(); 350 | } 351 | 352 | /// Starts listening for an interrupt event 353 | pub fn listen(&mut self, event: Event) { 354 | match event { 355 | Event::Rxne => { 356 | self.usart.cr1.modify(|_, w| w.rxneie().set_bit()) 357 | }, 358 | Event::Txe => { 359 | self.usart.cr1.modify(|_, w| w.txeie().set_bit()) 360 | }, 361 | Event::Idle => { 362 | self.usart.cr1.modify(|_, w| w.idleie().set_bit()) 363 | }, 364 | } 365 | } 366 | 367 | /// Stop listening for an interrupt event 368 | pub fn unlisten(&mut self, event: Event) { 369 | match event { 370 | Event::Rxne => { 371 | self.usart.cr1.modify(|_, w| w.rxneie().clear_bit()) 372 | }, 373 | Event::Txe => { 374 | self.usart.cr1.modify(|_, w| w.txeie().clear_bit()) 375 | }, 376 | Event::Idle => { 377 | self.usart.cr1.modify(|_, w| w.idleie().clear_bit()) 378 | }, 379 | } 380 | } 381 | 382 | /// Returns true if the line idle status is set 383 | pub fn is_idle(&self) -> bool { 384 | self.usart.isr.read().idle().bit_is_set() 385 | } 386 | 387 | /// Returns true if the tx register is empty 388 | pub fn is_txe(&self) -> bool { 389 | self.usart.isr.read().txe().bit_is_set() 390 | } 391 | 392 | /// Returns true if the rx register is not empty (and can be read) 393 | pub fn is_rx_not_empty(&self) -> bool { 394 | self.usart.isr.read().rxne().bit_is_set() 395 | } 396 | 397 | /// Returns true if transmission is complete 398 | pub fn is_tx_complete(&self) -> bool { 399 | self.usart.isr.read().tc().bit_is_set() 400 | } 401 | } 402 | )+ 403 | } 404 | } 405 | 406 | usart! { 407 | USART1: (usart1, usart1tx, usart1rx, usart1en, apb2enr), 408 | } 409 | #[cfg(any( 410 | feature = "stm32f030x8", 411 | feature = "stm32f030xc", 412 | feature = "stm32f042", 413 | feature = "stm32f048", 414 | feature = "stm32f051", 415 | feature = "stm32f058", 416 | feature = "stm32f070", 417 | feature = "stm32f071", 418 | feature = "stm32f072", 419 | feature = "stm32f078", 420 | feature = "stm32f091", 421 | feature = "stm32f098", 422 | ))] 423 | usart! { 424 | USART2: (usart2, usart2tx, usart2rx,usart2en, apb1enr), 425 | } 426 | #[cfg(any( 427 | feature = "stm32f030xc", 428 | feature = "stm32f070xb", 429 | feature = "stm32f071", 430 | feature = "stm32f072", 431 | feature = "stm32f078", 432 | feature = "stm32f091", 433 | feature = "stm32f098", 434 | ))] 435 | usart! { 436 | USART3: (usart3, usart3tx, usart3rx,usart3en, apb1enr), 437 | USART4: (usart4, usart4tx, usart4rx,usart4en, apb1enr), 438 | } 439 | #[cfg(any(feature = "stm32f030xc", feature = "stm32f091", feature = "stm32f098"))] 440 | usart! { 441 | USART5: (usart5, usart5tx, usart5rx,usart5en, apb1enr), 442 | USART6: (usart6, usart6tx, usart6rx,usart6en, apb2enr), 443 | } 444 | 445 | impl embedded_hal::serial::Read for Rx 446 | where 447 | USART: Deref, 448 | { 449 | type Error = Error; 450 | 451 | /// Tries to read a byte from the uart 452 | fn read(&mut self) -> nb::Result { 453 | read(self.usart) 454 | } 455 | } 456 | 457 | impl embedded_hal::serial::Read for Serial 458 | where 459 | USART: Deref, 460 | RXPIN: RxPin, 461 | { 462 | type Error = Error; 463 | 464 | /// Tries to read a byte from the uart 465 | fn read(&mut self) -> nb::Result { 466 | read(&*self.usart) 467 | } 468 | } 469 | 470 | impl embedded_hal::serial::Write for Tx 471 | where 472 | USART: Deref, 473 | { 474 | type Error = Infallible; 475 | 476 | /// Ensures that none of the previously written words are still buffered 477 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 478 | flush(self.usart) 479 | } 480 | 481 | /// Tries to write a byte to the uart 482 | /// Fails if the transmit buffer is full 483 | fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { 484 | write(self.usart, byte) 485 | } 486 | } 487 | 488 | impl embedded_hal::serial::Write for Serial 489 | where 490 | USART: Deref, 491 | TXPIN: TxPin, 492 | { 493 | type Error = Infallible; 494 | 495 | /// Ensures that none of the previously written words are still buffered 496 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 497 | flush(&*self.usart) 498 | } 499 | 500 | /// Tries to write a byte to the uart 501 | /// Fails if the transmit buffer is full 502 | fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { 503 | write(&*self.usart, byte) 504 | } 505 | } 506 | 507 | impl Serial 508 | where 509 | USART: Deref, 510 | { 511 | /// Splits the UART Peripheral in a Tx and an Rx part 512 | /// This is required for sending/receiving 513 | pub fn split(self) -> (Tx, Rx) 514 | where 515 | TXPIN: TxPin, 516 | RXPIN: RxPin, 517 | { 518 | ( 519 | Tx { 520 | usart: &*self.usart, 521 | _instance: PhantomData, 522 | }, 523 | Rx { 524 | usart: &*self.usart, 525 | _instance: PhantomData, 526 | }, 527 | ) 528 | } 529 | 530 | pub fn release(self) -> (USART, (TXPIN, RXPIN)) { 531 | (self.usart, self.pins) 532 | } 533 | } 534 | 535 | impl Write for Tx 536 | where 537 | Tx: embedded_hal::serial::Write, 538 | { 539 | fn write_str(&mut self, s: &str) -> Result { 540 | s.as_bytes() 541 | .iter() 542 | .try_for_each(|c| nb::block!(self.write(*c))) 543 | .map_err(|_| core::fmt::Error) 544 | } 545 | } 546 | 547 | impl Write for Serial 548 | where 549 | USART: Deref, 550 | TXPIN: TxPin, 551 | { 552 | fn write_str(&mut self, s: &str) -> Result { 553 | s.as_bytes() 554 | .iter() 555 | .try_for_each(|c| nb::block!(self.write(*c))) 556 | .map_err(|_| core::fmt::Error) 557 | } 558 | } 559 | 560 | /// Ensures that none of the previously written words are still buffered 561 | fn flush(usart: *const SerialRegisterBlock) -> nb::Result<(), Infallible> { 562 | // NOTE(unsafe) atomic read with no side effects 563 | let isr = unsafe { (*usart).isr.read() }; 564 | 565 | if isr.tc().bit_is_set() { 566 | Ok(()) 567 | } else { 568 | Err(nb::Error::WouldBlock) 569 | } 570 | } 571 | 572 | /// Tries to write a byte to the UART 573 | /// Returns `Err(WouldBlock)` if the transmit buffer is full 574 | fn write(usart: *const SerialRegisterBlock, byte: u8) -> nb::Result<(), Infallible> { 575 | // NOTE(unsafe) atomic read with no side effects 576 | let isr = unsafe { (*usart).isr.read() }; 577 | 578 | if isr.txe().bit_is_set() { 579 | // NOTE(unsafe) atomic write to stateless register 580 | unsafe { (*usart).tdr.write(|w| w.tdr().bits(byte as u16)) } 581 | Ok(()) 582 | } else { 583 | Err(nb::Error::WouldBlock) 584 | } 585 | } 586 | 587 | /// Tries to read a byte from the UART 588 | fn read(usart: *const SerialRegisterBlock) -> nb::Result { 589 | // NOTE(unsafe) atomic read with no side effects 590 | let isr = unsafe { (*usart).isr.read() }; 591 | 592 | // NOTE(unsafe) write accessor for atomic writes with no side effects 593 | let icr = unsafe { &(*usart).icr }; 594 | 595 | if isr.pe().bit_is_set() { 596 | icr.write(|w| w.pecf().set_bit()); 597 | Err(nb::Error::Other(Error::Parity)) 598 | } else if isr.fe().bit_is_set() { 599 | icr.write(|w| w.fecf().set_bit()); 600 | Err(nb::Error::Other(Error::Framing)) 601 | } else if isr.nf().bit_is_set() { 602 | icr.write(|w| w.ncf().set_bit()); 603 | Err(nb::Error::Other(Error::Noise)) 604 | } else if isr.ore().bit_is_set() { 605 | icr.write(|w| w.orecf().set_bit()); 606 | Err(nb::Error::Other(Error::Overrun)) 607 | } else if isr.rxne().bit_is_set() { 608 | Ok(unsafe { (*usart).rdr.read().rdr().bits() as u8 }) 609 | } else { 610 | Err(nb::Error::WouldBlock) 611 | } 612 | } 613 | -------------------------------------------------------------------------------- /src/signature.rs: -------------------------------------------------------------------------------- 1 | //! Device electronic signature 2 | //! 3 | //! (stored in flash memory) 4 | 5 | /// This is the test voltage in millivolts of the calibration done at the factory 6 | pub const VDDA_CALIB: u32 = 3300; 7 | 8 | macro_rules! define_ptr_type { 9 | ($name: ident, $ptr: expr) => { 10 | impl $name { 11 | fn ptr() -> *const Self { 12 | $ptr as *const _ 13 | } 14 | 15 | /// Returns a wrapped reference to the value in flash memory 16 | pub fn get() -> &'static Self { 17 | unsafe { &*Self::ptr() } 18 | } 19 | } 20 | }; 21 | } 22 | 23 | // f030 and f070 don't have a UID in ROM 24 | #[cfg(not(any(feature = "stm32f030", feature = "stm32f070")))] 25 | #[derive(Hash, Debug)] 26 | #[repr(C)] 27 | pub struct Uid { 28 | x: u16, 29 | y: u16, 30 | waf_lot: [u8; 8], 31 | } 32 | #[cfg(not(any(feature = "stm32f030", feature = "stm32f070")))] 33 | define_ptr_type!(Uid, 0x1FFF_F7AC); 34 | 35 | /// Device UID from ROM. See the [reference manual](https://www.st.com/content/ccc/resource/technical/document/reference_manual/c2/f8/8a/f2/18/e6/43/96/DM00031936.pdf/files/DM00031936.pdf/jcr:content/translations/en.DM00031936.pdf#%5B%7B%22num%22%3A1575%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C67%2C755%2Cnull%5D) for more info. 36 | #[cfg(not(any(feature = "stm32f030", feature = "stm32f070")))] 37 | impl Uid { 38 | /// X coordinate on wafer 39 | pub fn x(&self) -> u16 { 40 | self.x 41 | } 42 | 43 | /// Y coordinate on wafer 44 | pub fn y(&self) -> u16 { 45 | self.y 46 | } 47 | 48 | /// Wafer number 49 | pub fn waf_num(&self) -> u8 { 50 | self.waf_lot[0] 51 | } 52 | 53 | /// Lot number 54 | pub fn lot_num(&self) -> &str { 55 | unsafe { core::str::from_utf8_unchecked(&self.waf_lot[1..]) } 56 | } 57 | } 58 | 59 | /// Size of integrated flash 60 | #[derive(Debug)] 61 | #[repr(C)] 62 | pub struct FlashSize(u16); 63 | #[cfg(not(any(feature = "stm32f030", feature = "stm32f070")))] 64 | define_ptr_type!(FlashSize, 0x1FFF_F7CC); 65 | #[cfg(any(feature = "stm32f030", feature = "stm32f070"))] 66 | define_ptr_type!(FlashSize, 0x1FFF_0000); 67 | 68 | impl FlashSize { 69 | /// Read flash size in kilobytes 70 | pub fn kilo_bytes(&self) -> u16 { 71 | self.0 72 | } 73 | 74 | /// Read flash size in bytes 75 | pub fn bytes(&self) -> usize { 76 | usize::from(self.kilo_bytes()) * 1024 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/spi.rs: -------------------------------------------------------------------------------- 1 | //! API for the integrate SPI peripherals 2 | //! 3 | //! The spi bus acts as the master (generating the clock) and you need to handle the CS separately. 4 | //! 5 | //! The most significant bit is transmitted first & only 8-bit transfers are supported 6 | //! 7 | //! # Example 8 | //! Echo incoming data in the next transfer 9 | //! ``` no_run 10 | //! use stm32f0xx_hal as hal; 11 | //! 12 | //! use crate::hal::pac; 13 | //! use crate::hal::prelude::*; 14 | //! use crate::hal::spi::{Spi, Mode, Phase, Polarity}; 15 | //! 16 | //! cortex_m::interrupt::free(|cs| { 17 | //! let mut p = pac::Peripherals::take().unwrap(); 18 | //! let mut rcc = p.RCC.constrain().freeze(&mut p.FLASH); 19 | //! 20 | //! let gpioa = p.GPIOA.split(&mut rcc); 21 | //! 22 | //! // Configure pins for SPI 23 | //! let sck = gpioa.pa5.into_alternate_af0(cs); 24 | //! let miso = gpioa.pa6.into_alternate_af0(cs); 25 | //! let mosi = gpioa.pa7.into_alternate_af0(cs); 26 | //! 27 | //! // Configure SPI with 1MHz rate 28 | //! let mut spi = Spi::spi1(p.SPI1, (sck, miso, mosi), Mode { 29 | //! polarity: Polarity::IdleHigh, 30 | //! phase: Phase::CaptureOnSecondTransition, 31 | //! }, 1.mhz(), &mut rcc); 32 | //! 33 | //! let mut data = [0]; 34 | //! loop { 35 | //! spi.transfer(&mut data).unwrap(); 36 | //! } 37 | //! }); 38 | //! ``` 39 | 40 | use core::marker::PhantomData; 41 | use core::{ops::Deref, ptr}; 42 | 43 | pub use embedded_hal::spi::{Mode, Phase, Polarity}; 44 | 45 | // TODO Put this inside the macro 46 | // Currently that causes a compiler panic 47 | use crate::pac::SPI1; 48 | #[cfg(any( 49 | feature = "stm32f030x8", 50 | feature = "stm32f030xc", 51 | feature = "stm32f042", 52 | feature = "stm32f048", 53 | feature = "stm32f051", 54 | feature = "stm32f058", 55 | feature = "stm32f070xb", 56 | feature = "stm32f071", 57 | feature = "stm32f072", 58 | feature = "stm32f078", 59 | feature = "stm32f091", 60 | feature = "stm32f098", 61 | ))] 62 | use crate::pac::SPI2; 63 | 64 | use crate::gpio::*; 65 | 66 | use crate::rcc::{Clocks, Rcc}; 67 | 68 | use crate::time::Hertz; 69 | 70 | /// Typestate for 8-bit transfer size 71 | pub struct EightBit; 72 | 73 | /// Typestate for 16-bit transfer size 74 | pub struct SixteenBit; 75 | 76 | /// SPI error 77 | #[non_exhaustive] 78 | #[derive(Debug)] 79 | pub enum Error { 80 | /// Overrun occurred 81 | Overrun, 82 | /// Mode fault occurred 83 | ModeFault, 84 | /// CRC error 85 | Crc, 86 | } 87 | 88 | /// SPI abstraction 89 | pub struct Spi { 90 | spi: SPI, 91 | pins: (SCKPIN, MISOPIN, MOSIPIN), 92 | _width: PhantomData, 93 | } 94 | 95 | pub trait SckPin {} 96 | pub trait MisoPin {} 97 | pub trait MosiPin {} 98 | 99 | macro_rules! spi_pins { 100 | ($($SPI:ident => { 101 | sck => [$($sck:ty),+ $(,)*], 102 | miso => [$($miso:ty),+ $(,)*], 103 | mosi => [$($mosi:ty),+ $(,)*], 104 | })+) => { 105 | $( 106 | $( 107 | impl SckPin for $sck {} 108 | )+ 109 | $( 110 | impl MisoPin for $miso {} 111 | )+ 112 | $( 113 | impl MosiPin for $mosi {} 114 | )+ 115 | )+ 116 | } 117 | } 118 | 119 | spi_pins! { 120 | SPI1 => { 121 | sck => [gpioa::PA5>, gpiob::PB3>], 122 | miso => [gpioa::PA6>, gpiob::PB4>], 123 | mosi => [gpioa::PA7>, gpiob::PB5>], 124 | } 125 | } 126 | #[cfg(any( 127 | feature = "stm32f030x4", 128 | feature = "stm32f030x6", 129 | feature = "stm32f031", 130 | feature = "stm32f038", 131 | ))] 132 | spi_pins! { 133 | SPI1 => { 134 | sck => [gpiob::PB13>], 135 | miso => [gpiob::PB14>], 136 | mosi => [gpiob::PB15>], 137 | } 138 | } 139 | // TODO: The ST SVD files are missing the entire PE enable register. 140 | // So those pins do not exist in the register definitions. 141 | // Re-enable as soon as this gets fixed. 142 | // #[cfg(any( 143 | // feature = "stm32f071", 144 | // feature = "stm32f072", 145 | // feature = "stm32f078", 146 | // feature = "stm32f091", 147 | // feature = "stm32f098", 148 | // ))] 149 | // spi_pins! { 150 | // SPI1 => { 151 | // sck => [gpioe::PE13>], 152 | // miso => [gpioe::PE14>], 153 | // mosi => [gpioe::PE15>], 154 | // } 155 | // } 156 | 157 | #[cfg(any( 158 | feature = "stm32f030x8", 159 | feature = "stm32f030xc", 160 | feature = "stm32f042", 161 | feature = "stm32f048", 162 | feature = "stm32f051", 163 | feature = "stm32f058", 164 | feature = "stm32f070xb", 165 | feature = "stm32f071", 166 | feature = "stm32f072", 167 | feature = "stm32f078", 168 | feature = "stm32f091", 169 | feature = "stm32f098", 170 | ))] 171 | spi_pins! { 172 | SPI2 => { 173 | sck => [gpiob::PB13>], 174 | miso => [gpiob::PB14>], 175 | mosi => [gpiob::PB15>], 176 | } 177 | } 178 | #[cfg(any( 179 | feature = "stm32f030xc", 180 | feature = "stm32f070xb", 181 | feature = "stm32f071", 182 | feature = "stm32f072", 183 | feature = "stm32f078", 184 | feature = "stm32f091", 185 | feature = "stm32f098", 186 | ))] 187 | spi_pins! { 188 | SPI2 => { 189 | sck => [gpiob::PB10>], 190 | miso => [gpioc::PC2>], 191 | mosi => [gpioc::PC3>], 192 | } 193 | } 194 | #[cfg(any( 195 | feature = "stm32f071", 196 | feature = "stm32f072", 197 | feature = "stm32f078", 198 | feature = "stm32f091", 199 | feature = "stm32f098", 200 | ))] 201 | spi_pins! { 202 | SPI2 => { 203 | sck => [gpiod::PD1>], 204 | miso => [gpiod::PD3>], 205 | mosi => [gpiod::PD4>], 206 | } 207 | } 208 | 209 | macro_rules! spi { 210 | ($($SPI:ident: ($spi:ident, $spiXen:ident, $spiXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => { 211 | $( 212 | impl Spi<$SPI, SCKPIN, MISOPIN, MOSIPIN, EightBit> { 213 | /// Creates a new spi instance 214 | pub fn $spi( 215 | spi: $SPI, 216 | pins: (SCKPIN, MISOPIN, MOSIPIN), 217 | mode: Mode, 218 | speed: F, 219 | rcc: &mut Rcc, 220 | ) -> Self 221 | where 222 | SCKPIN: SckPin<$SPI>, 223 | MISOPIN: MisoPin<$SPI>, 224 | MOSIPIN: MosiPin<$SPI>, 225 | F: Into, 226 | { 227 | /* Enable clock for SPI */ 228 | rcc.regs.$apbenr.modify(|_, w| w.$spiXen().set_bit()); 229 | 230 | /* Reset SPI */ 231 | rcc.regs.$apbrstr.modify(|_, w| w.$spiXrst().set_bit()); 232 | rcc.regs.$apbrstr.modify(|_, w| w.$spiXrst().clear_bit()); 233 | 234 | Spi::<$SPI, SCKPIN, MISOPIN, MOSIPIN, EightBit> { spi, pins, _width: PhantomData }.spi_init(mode, speed, rcc.clocks).into_8bit_width() 235 | } 236 | } 237 | )+ 238 | } 239 | } 240 | 241 | spi! { 242 | SPI1: (spi1, spi1en, spi1rst, apb2enr, apb2rstr), 243 | } 244 | #[cfg(any( 245 | feature = "stm32f030x8", 246 | feature = "stm32f030xc", 247 | feature = "stm32f042", 248 | feature = "stm32f048", 249 | feature = "stm32f051", 250 | feature = "stm32f058", 251 | feature = "stm32f070xb", 252 | feature = "stm32f071", 253 | feature = "stm32f072", 254 | feature = "stm32f078", 255 | feature = "stm32f091", 256 | feature = "stm32f098", 257 | ))] 258 | spi! { 259 | SPI2: (spi2, spi2en, spi2rst, apb1enr, apb1rstr), 260 | } 261 | 262 | // It's s needed for the impls, but rustc doesn't recognize that 263 | #[allow(dead_code)] 264 | type SpiRegisterBlock = crate::pac::spi1::RegisterBlock; 265 | 266 | impl Spi 267 | where 268 | SPI: Deref, 269 | { 270 | fn spi_init(self, mode: Mode, speed: F, clocks: Clocks) -> Self 271 | where 272 | F: Into, 273 | { 274 | /* Make sure the SPI unit is disabled so we can configure it */ 275 | self.spi.cr1.modify(|_, w| w.spe().clear_bit()); 276 | 277 | let br = match clocks.pclk().0 / speed.into().0 { 278 | 0 => unreachable!(), 279 | 1..=2 => 0b000, 280 | 3..=5 => 0b001, 281 | 6..=11 => 0b010, 282 | 12..=23 => 0b011, 283 | 24..=47 => 0b100, 284 | 48..=95 => 0b101, 285 | 96..=191 => 0b110, 286 | _ => 0b111, 287 | }; 288 | 289 | // mstr: master configuration 290 | // lsbfirst: MSB first 291 | // ssm: enable software slave management (NSS pin free for other uses) 292 | // ssi: set nss high = master mode 293 | // dff: 8 bit frames 294 | // bidimode: 2-line unidirectional 295 | // spe: enable the SPI bus 296 | self.spi.cr1.write(|w| { 297 | w.cpha() 298 | .bit(mode.phase == Phase::CaptureOnSecondTransition) 299 | .cpol() 300 | .bit(mode.polarity == Polarity::IdleHigh) 301 | .mstr() 302 | .set_bit() 303 | .br() 304 | .bits(br) 305 | .lsbfirst() 306 | .clear_bit() 307 | .ssm() 308 | .set_bit() 309 | .ssi() 310 | .set_bit() 311 | .rxonly() 312 | .clear_bit() 313 | .bidimode() 314 | .clear_bit() 315 | .spe() 316 | .set_bit() 317 | }); 318 | 319 | self 320 | } 321 | 322 | pub fn into_8bit_width(self) -> Spi { 323 | // FRXTH: 8-bit threshold on RX FIFO 324 | // DS: 8-bit data size 325 | // SSOE: cleared to disable SS output 326 | self.spi 327 | .cr2 328 | .write(|w| w.frxth().set_bit().ds().eight_bit().ssoe().clear_bit()); 329 | 330 | Spi { 331 | spi: self.spi, 332 | pins: self.pins, 333 | _width: PhantomData, 334 | } 335 | } 336 | 337 | pub fn into_16bit_width(self) -> Spi { 338 | // FRXTH: 16-bit threshold on RX FIFO 339 | // DS: 8-bit data size 340 | // SSOE: cleared to disable SS output 341 | self.spi 342 | .cr2 343 | .write(|w| w.frxth().set_bit().ds().sixteen_bit().ssoe().clear_bit()); 344 | 345 | Spi { 346 | spi: self.spi, 347 | pins: self.pins, 348 | _width: PhantomData, 349 | } 350 | } 351 | 352 | fn set_send_only(&mut self) { 353 | self.spi 354 | .cr1 355 | .modify(|_, w| w.bidimode().set_bit().bidioe().set_bit()); 356 | } 357 | 358 | fn set_bidi(&mut self) { 359 | self.spi 360 | .cr1 361 | .modify(|_, w| w.bidimode().clear_bit().bidioe().clear_bit()); 362 | } 363 | 364 | fn check_read(&mut self) -> nb::Result<(), Error> { 365 | let sr = self.spi.sr.read(); 366 | 367 | Err(if sr.ovr().bit_is_set() { 368 | nb::Error::Other(Error::Overrun) 369 | } else if sr.modf().bit_is_set() { 370 | nb::Error::Other(Error::ModeFault) 371 | } else if sr.crcerr().bit_is_set() { 372 | nb::Error::Other(Error::Crc) 373 | } else if sr.rxne().bit_is_set() { 374 | return Ok(()); 375 | } else { 376 | nb::Error::WouldBlock 377 | }) 378 | } 379 | 380 | fn send_buffer_size(&mut self) -> u8 { 381 | match self.spi.sr.read().ftlvl().bits() { 382 | // FIFO empty 383 | 0 => 4, 384 | // FIFO 1/4 full 385 | 1 => 3, 386 | // FIFO 1/2 full 387 | 2 => 2, 388 | // FIFO full 389 | _ => 0, 390 | } 391 | } 392 | 393 | fn check_send(&mut self) -> nb::Result<(), Error> { 394 | let sr = self.spi.sr.read(); 395 | 396 | Err(if sr.ovr().bit_is_set() { 397 | nb::Error::Other(Error::Overrun) 398 | } else if sr.modf().bit_is_set() { 399 | nb::Error::Other(Error::ModeFault) 400 | } else if sr.crcerr().bit_is_set() { 401 | nb::Error::Other(Error::Crc) 402 | } else if sr.txe().bit_is_set() && sr.bsy().bit_is_clear() { 403 | return Ok(()); 404 | } else { 405 | nb::Error::WouldBlock 406 | }) 407 | } 408 | 409 | fn read_u8(&mut self) -> u8 { 410 | // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows reading a half-word) 411 | unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const u8) } 412 | } 413 | 414 | fn send_u8(&mut self, byte: u8) { 415 | // NOTE(write_volatile) see note above 416 | unsafe { ptr::write_volatile(ptr::addr_of!(self.spi.dr) as *mut u8, byte) } 417 | } 418 | 419 | fn read_u16(&mut self) -> u16 { 420 | // NOTE(read_volatile) read only 2 bytes (the svd2rust API only allows reading a half-word) 421 | unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const u16) } 422 | } 423 | 424 | fn send_u16(&mut self, byte: u16) { 425 | // NOTE(write_volatile) see note above 426 | unsafe { ptr::write_volatile(ptr::addr_of!(self.spi.dr) as *mut u16, byte) } 427 | } 428 | 429 | pub fn release(self) -> (SPI, (SCKPIN, MISOPIN, MOSIPIN)) { 430 | (self.spi, self.pins) 431 | } 432 | } 433 | 434 | impl ::embedded_hal::blocking::spi::Transfer 435 | for Spi 436 | where 437 | SPI: Deref, 438 | { 439 | type Error = Error; 440 | 441 | fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { 442 | // We want to transfer bidirectionally, make sure we're in the correct mode 443 | self.set_bidi(); 444 | 445 | for word in words.iter_mut() { 446 | nb::block!(self.check_send())?; 447 | self.send_u8(*word); 448 | nb::block!(self.check_read())?; 449 | *word = self.read_u8(); 450 | } 451 | 452 | Ok(words) 453 | } 454 | } 455 | 456 | impl ::embedded_hal::blocking::spi::Write 457 | for Spi 458 | where 459 | SPI: Deref, 460 | { 461 | type Error = Error; 462 | 463 | fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { 464 | let mut bufcap: u8 = 0; 465 | 466 | // We only want to send, so we don't need to worry about the receive buffer overflowing 467 | self.set_send_only(); 468 | 469 | // Make sure we don't continue with an error condition 470 | nb::block!(self.check_send())?; 471 | 472 | // We have a 32 bit buffer to work with, so let's fill it before checking the status 473 | for word in words { 474 | // Loop as long as our send buffer is full 475 | while bufcap == 0 { 476 | bufcap = self.send_buffer_size(); 477 | } 478 | 479 | self.send_u8(*word); 480 | bufcap -= 1; 481 | } 482 | 483 | // Do one last status register check before continuing 484 | nb::block!(self.check_send()).ok(); 485 | Ok(()) 486 | } 487 | } 488 | 489 | impl ::embedded_hal::blocking::spi::Transfer 490 | for Spi 491 | where 492 | SPI: Deref, 493 | { 494 | type Error = Error; 495 | 496 | fn transfer<'w>(&mut self, words: &'w mut [u16]) -> Result<&'w [u16], Self::Error> { 497 | // We want to transfer bidirectionally, make sure we're in the correct mode 498 | self.set_bidi(); 499 | 500 | for word in words.iter_mut() { 501 | nb::block!(self.check_send())?; 502 | self.send_u16(*word); 503 | nb::block!(self.check_read())?; 504 | *word = self.read_u16(); 505 | } 506 | 507 | Ok(words) 508 | } 509 | } 510 | 511 | impl ::embedded_hal::blocking::spi::Write 512 | for Spi 513 | where 514 | SPI: Deref, 515 | { 516 | type Error = Error; 517 | 518 | fn write(&mut self, words: &[u16]) -> Result<(), Self::Error> { 519 | // We only want to send, so we don't need to worry about the receive buffer overflowing 520 | self.set_send_only(); 521 | 522 | for word in words { 523 | nb::block!(self.check_send())?; 524 | self.send_u16(*word); 525 | } 526 | 527 | // Do one last status register check before continuing 528 | nb::block!(self.check_send()).ok(); 529 | Ok(()) 530 | } 531 | } 532 | 533 | impl embedded_hal_1::spi::Error for Error { 534 | fn kind(&self) -> embedded_hal_1::spi::ErrorKind { 535 | match self { 536 | Error::Overrun => embedded_hal_1::spi::ErrorKind::Overrun, 537 | Error::ModeFault => embedded_hal_1::spi::ErrorKind::ModeFault, 538 | Error::Crc => embedded_hal_1::spi::ErrorKind::Other, 539 | } 540 | } 541 | } 542 | impl embedded_hal_1::spi::ErrorType 543 | for Spi 544 | where 545 | SPI: Deref, 546 | { 547 | type Error = Error; 548 | } 549 | 550 | impl embedded_hal_1::spi::SpiBus 551 | for Spi 552 | where 553 | SPI: Deref, 554 | { 555 | fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { 556 | // We want to transfer bidirectionally, make sure we're in the correct mode 557 | self.set_bidi(); 558 | 559 | for word in words.iter_mut() { 560 | nb::block!(self.check_send())?; 561 | self.send_u8(0); // FIXME is this necessary? 562 | nb::block!(self.check_read())?; 563 | *word = self.read_u8(); 564 | } 565 | Ok(()) 566 | } 567 | 568 | fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { 569 | embedded_hal::blocking::spi::Write::write(self, words) 570 | } 571 | 572 | fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { 573 | // We want to transfer bidirectionally, make sure we're in the correct mode 574 | self.set_bidi(); 575 | 576 | for (w, r) in write.iter().zip(read.iter_mut()) { 577 | nb::block!(self.check_send())?; 578 | self.send_u8(*w); 579 | nb::block!(self.check_read())?; 580 | *r = self.read_u8(); 581 | } 582 | Ok(()) 583 | } 584 | 585 | fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { 586 | embedded_hal::blocking::spi::Transfer::transfer(self, words).map(|_| ()) 587 | } 588 | 589 | fn flush(&mut self) -> Result<(), Self::Error> { 590 | Ok(()) 591 | } 592 | } 593 | 594 | impl embedded_hal_1::spi::SpiBus 595 | for Spi 596 | where 597 | SPI: Deref, 598 | { 599 | fn read(&mut self, words: &mut [u16]) -> Result<(), Self::Error> { 600 | // We want to transfer bidirectionally, make sure we're in the correct mode 601 | self.set_bidi(); 602 | 603 | for word in words.iter_mut() { 604 | nb::block!(self.check_send())?; 605 | self.send_u16(0); // FIXME is this necessary? 606 | nb::block!(self.check_read())?; 607 | *word = self.read_u16(); 608 | } 609 | Ok(()) 610 | } 611 | 612 | fn write(&mut self, words: &[u16]) -> Result<(), Self::Error> { 613 | embedded_hal::blocking::spi::Write::write(self, words) 614 | } 615 | 616 | fn transfer(&mut self, read: &mut [u16], write: &[u16]) -> Result<(), Self::Error> { 617 | // We want to transfer bidirectionally, make sure we're in the correct mode 618 | self.set_bidi(); 619 | 620 | for (w, r) in write.iter().zip(read.iter_mut()) { 621 | nb::block!(self.check_send())?; 622 | self.send_u16(*w); 623 | nb::block!(self.check_read())?; 624 | *r = self.read_u16(); 625 | } 626 | Ok(()) 627 | } 628 | 629 | fn transfer_in_place(&mut self, words: &mut [u16]) -> Result<(), Self::Error> { 630 | embedded_hal::blocking::spi::Transfer::transfer(self, words).map(|_| ()) 631 | } 632 | 633 | fn flush(&mut self) -> Result<(), Self::Error> { 634 | Ok(()) 635 | } 636 | } 637 | -------------------------------------------------------------------------------- /src/time.rs: -------------------------------------------------------------------------------- 1 | /// Bits per second 2 | #[derive(PartialEq, PartialOrd, Clone, Copy)] 3 | pub struct Bps(pub u32); 4 | 5 | #[derive(PartialEq, PartialOrd, Clone, Copy)] 6 | pub struct Hertz(pub u32); 7 | 8 | #[derive(PartialEq, PartialOrd, Clone, Copy)] 9 | pub struct KiloHertz(pub u32); 10 | 11 | #[derive(PartialEq, PartialOrd, Clone, Copy)] 12 | pub struct MegaHertz(pub u32); 13 | 14 | /// Extension trait that adds convenience methods to the `u32` type 15 | pub trait U32Ext { 16 | /// Wrap in `Bps` 17 | fn bps(self) -> Bps; 18 | 19 | /// Wrap in `Hertz` 20 | fn hz(self) -> Hertz; 21 | 22 | /// Wrap in `KiloHertz` 23 | fn khz(self) -> KiloHertz; 24 | 25 | /// Wrap in `MegaHertz` 26 | fn mhz(self) -> MegaHertz; 27 | } 28 | 29 | impl U32Ext for u32 { 30 | fn bps(self) -> Bps { 31 | Bps(self) 32 | } 33 | 34 | fn hz(self) -> Hertz { 35 | Hertz(self) 36 | } 37 | 38 | fn khz(self) -> KiloHertz { 39 | KiloHertz(self) 40 | } 41 | 42 | fn mhz(self) -> MegaHertz { 43 | MegaHertz(self) 44 | } 45 | } 46 | 47 | impl From for Hertz { 48 | fn from(khz: KiloHertz) -> Self { 49 | Hertz(khz.0 * 1_000) 50 | } 51 | } 52 | 53 | impl From for Hertz { 54 | fn from(mhz: MegaHertz) -> Self { 55 | Hertz(mhz.0 * 1_000_000) 56 | } 57 | } 58 | 59 | impl From for KiloHertz { 60 | fn from(mhz: MegaHertz) -> Self { 61 | KiloHertz(mhz.0 * 1_000) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/timers.rs: -------------------------------------------------------------------------------- 1 | //! API for the integrated timers 2 | //! 3 | //! This only implements basic functions, a lot of things are missing 4 | //! 5 | //! # Example 6 | //! Blink the led with 1Hz 7 | //! ``` no_run 8 | //! use stm32f0xx_hal as hal; 9 | //! 10 | //! use crate::hal::pac; 11 | //! use crate::hal::prelude::*; 12 | //! use crate::hal::time::*; 13 | //! use crate::hal::timers::*; 14 | //! use nb::block; 15 | //! 16 | //! cortex_m::interrupt::free(|cs| { 17 | //! let mut p = pac::Peripherals::take().unwrap(); 18 | //! let mut rcc = p.RCC.configure().freeze(&mut p.FLASH); 19 | //! 20 | //! let gpioa = p.GPIOA.split(&mut rcc); 21 | //! 22 | //! let mut led = gpioa.pa1.into_push_pull_pull_output(cs); 23 | //! 24 | //! let mut timer = Timer::tim1(p.TIM1, Hertz(1), &mut rcc); 25 | //! loop { 26 | //! led.toggle(); 27 | //! block!(timer.wait()).ok(); 28 | //! } 29 | //! }); 30 | //! ``` 31 | use cortex_m::peripheral::syst::SystClkSource; 32 | use cortex_m::peripheral::SYST; 33 | 34 | use crate::rcc::{Clocks, Rcc}; 35 | 36 | use crate::time::Hertz; 37 | use embedded_hal::timer::{CountDown, Periodic}; 38 | use void::Void; 39 | 40 | /// Hardware timers 41 | pub struct Timer { 42 | clocks: Clocks, 43 | tim: TIM, 44 | } 45 | 46 | /// Interrupt events 47 | pub enum Event { 48 | /// Timer timed out / count down ended 49 | TimeOut, 50 | } 51 | 52 | impl Timer { 53 | /// Configures the SYST clock as a periodic count down timer 54 | pub fn syst(mut syst: SYST, timeout: T, rcc: &Rcc) -> Self 55 | where 56 | T: Into, 57 | { 58 | syst.set_clock_source(SystClkSource::Core); 59 | let mut timer = Timer { 60 | tim: syst, 61 | clocks: rcc.clocks, 62 | }; 63 | timer.start(timeout); 64 | timer 65 | } 66 | 67 | /// Starts listening for an `event` 68 | pub fn listen(&mut self, event: &Event) { 69 | match event { 70 | Event::TimeOut => self.tim.enable_interrupt(), 71 | } 72 | } 73 | 74 | /// Stops listening for an `event` 75 | pub fn unlisten(&mut self, event: &Event) { 76 | match event { 77 | Event::TimeOut => self.tim.disable_interrupt(), 78 | } 79 | } 80 | } 81 | 82 | /// Use the systick as a timer 83 | /// 84 | /// Be aware that intervals less than 4 Hertz may not function properly 85 | impl CountDown for Timer { 86 | type Time = Hertz; 87 | 88 | /// Start the timer with a `timeout` 89 | fn start(&mut self, timeout: T) 90 | where 91 | T: Into, 92 | { 93 | let rvr = self.clocks.sysclk().0 / timeout.into().0 - 1; 94 | 95 | assert!(rvr < (1 << 24)); 96 | 97 | self.tim.set_reload(rvr); 98 | self.tim.clear_current(); 99 | self.tim.enable_counter(); 100 | } 101 | 102 | /// Return `Ok` if the timer has wrapped 103 | /// Automatically clears the flag and restarts the time 104 | fn wait(&mut self) -> nb::Result<(), Void> { 105 | if self.tim.has_wrapped() { 106 | Ok(()) 107 | } else { 108 | Err(nb::Error::WouldBlock) 109 | } 110 | } 111 | } 112 | 113 | impl Periodic for Timer {} 114 | 115 | macro_rules! timers { 116 | ($($TIM:ident: ($tim:ident, $timXen:ident, $timXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => { 117 | $( 118 | use crate::pac::$TIM; 119 | impl Timer<$TIM> { 120 | // XXX(why not name this `new`?) bummer: constructors need to have different names 121 | // even if the `$TIM` are non overlapping (compare to the `free` function below 122 | // which just works) 123 | /// Configures a TIM peripheral as a periodic count down timer 124 | pub fn $tim(tim: $TIM, timeout: T, rcc: &mut Rcc) -> Self 125 | where 126 | T: Into, 127 | { 128 | // enable and reset peripheral to a clean slate state 129 | rcc.regs.$apbenr.modify(|_, w| w.$timXen().set_bit()); 130 | rcc.regs.$apbrstr.modify(|_, w| w.$timXrst().set_bit()); 131 | rcc.regs.$apbrstr.modify(|_, w| w.$timXrst().clear_bit()); 132 | 133 | let mut timer = Timer { 134 | clocks: rcc.clocks, 135 | tim, 136 | }; 137 | timer.start(timeout); 138 | 139 | timer 140 | } 141 | 142 | /// Starts listening for an `event` 143 | pub fn listen(&mut self, event: Event) { 144 | match event { 145 | Event::TimeOut => { 146 | // Enable update event interrupt 147 | self.tim.dier.write(|w| w.uie().set_bit()); 148 | } 149 | } 150 | } 151 | 152 | /// Stops listening for an `event` 153 | pub fn unlisten(&mut self, event: Event) { 154 | match event { 155 | Event::TimeOut => { 156 | // Enable update event interrupt 157 | self.tim.dier.write(|w| w.uie().clear_bit()); 158 | } 159 | } 160 | } 161 | 162 | /// Releases the TIM peripheral 163 | pub fn release(self) -> $TIM { 164 | let rcc = unsafe { &(*crate::pac::RCC::ptr()) }; 165 | // Pause counter 166 | self.tim.cr1.modify(|_, w| w.cen().clear_bit()); 167 | // Disable timer 168 | rcc.$apbenr.modify(|_, w| w.$timXen().clear_bit()); 169 | self.tim 170 | } 171 | 172 | /// Clears interrupt flag 173 | pub fn clear_irq(&mut self) { 174 | self.tim.sr.modify(|_, w| w.uif().clear_bit()); 175 | } 176 | } 177 | 178 | impl CountDown for Timer<$TIM> { 179 | type Time = Hertz; 180 | 181 | /// Start the timer with a `timeout` 182 | fn start(&mut self, timeout: T) 183 | where 184 | T: Into, 185 | { 186 | // pause 187 | self.tim.cr1.modify(|_, w| w.cen().clear_bit()); 188 | // restart counter 189 | self.tim.cnt.reset(); 190 | 191 | let frequency = timeout.into().0; 192 | // If pclk is prescaled from hclk, the frequency fed into the timers is doubled 193 | let tclk = if self.clocks.hclk().0 == self.clocks.pclk().0 { 194 | self.clocks.pclk().0 195 | } else { 196 | self.clocks.pclk().0 * 2 197 | }; 198 | let ticks = tclk / frequency; 199 | 200 | let psc = cast::u16((ticks - 1) / (1 << 16)).unwrap(); 201 | self.tim.psc.write(|w| w.psc().bits(psc)); 202 | 203 | let arr = cast::u16(ticks / cast::u32(psc + 1)).unwrap(); 204 | self.tim.arr.write(|w| unsafe { w.bits(cast::u32(arr)) }); 205 | 206 | // start counter 207 | self.tim.cr1.modify(|_, w| w.cen().set_bit()); 208 | } 209 | 210 | /// Return `Ok` if the timer has wrapped 211 | /// Automatically clears the flag and restarts the time 212 | fn wait(&mut self) -> nb::Result<(), Void> { 213 | if self.tim.sr.read().uif().bit_is_clear() { 214 | Err(nb::Error::WouldBlock) 215 | } else { 216 | self.tim.sr.modify(|_, w| w.uif().clear_bit()); 217 | Ok(()) 218 | } 219 | } 220 | } 221 | 222 | impl Periodic for Timer<$TIM> {} 223 | )+ 224 | } 225 | } 226 | 227 | timers! { 228 | TIM1: (tim1, tim1en, tim1rst, apb2enr, apb2rstr), 229 | TIM3: (tim3, tim3en, tim3rst, apb1enr, apb1rstr), 230 | TIM14: (tim14, tim14en, tim14rst, apb1enr, apb1rstr), 231 | TIM16: (tim16, tim16en, tim16rst, apb2enr, apb2rstr), 232 | TIM17: (tim17, tim17en, tim17rst, apb2enr, apb2rstr), 233 | } 234 | 235 | #[cfg(any( 236 | feature = "stm32f031", 237 | feature = "stm32f038", 238 | feature = "stm32f042", 239 | feature = "stm32f048", 240 | feature = "stm32f051", 241 | feature = "stm32f058", 242 | feature = "stm32f071", 243 | feature = "stm32f072", 244 | feature = "stm32f078", 245 | feature = "stm32f091", 246 | feature = "stm32f098", 247 | ))] 248 | timers! { 249 | TIM2: (tim2, tim2en, tim2rst, apb1enr, apb1rstr), 250 | } 251 | 252 | #[cfg(any( 253 | feature = "stm32f030x8", 254 | feature = "stm32f030xc", 255 | feature = "stm32f051", 256 | feature = "stm32f058", 257 | feature = "stm32f070xb", 258 | feature = "stm32f071", 259 | feature = "stm32f072", 260 | feature = "stm32f078", 261 | feature = "stm32f091", 262 | feature = "stm32f098", 263 | ))] 264 | timers! { 265 | TIM6: (tim6, tim6en, tim6rst, apb1enr, apb1rstr), 266 | TIM15: (tim15, tim15en, tim15rst, apb2enr, apb2rstr), 267 | } 268 | 269 | #[cfg(any( 270 | feature = "stm32f030xc", 271 | feature = "stm32f070xb", 272 | feature = "stm32f071", 273 | feature = "stm32f072", 274 | feature = "stm32f078", 275 | feature = "stm32f091", 276 | feature = "stm32f098", 277 | ))] 278 | timers! { 279 | TIM7: (tim7, tim7en, tim7rst, apb1enr, apb1rstr), 280 | } 281 | 282 | use crate::gpio::{AF0, AF1, AF2, AF4, AF5}; 283 | 284 | use crate::gpio::{gpioa::*, gpiob::*, Alternate}; 285 | 286 | // Output channels marker traits 287 | pub trait PinC1 {} 288 | pub trait PinC1N {} 289 | pub trait PinC2 {} 290 | pub trait PinC2N {} 291 | pub trait PinC3 {} 292 | pub trait PinC3N {} 293 | pub trait PinC4 {} 294 | 295 | macro_rules! channel_impl { 296 | ( $( $TIM:ident, $PINC:ident, $PINX:ident, $MODE:ident<$AF:ident>; )+ ) => { 297 | $( 298 | impl $PINC<$TIM> for $PINX<$MODE<$AF>> {} 299 | )+ 300 | }; 301 | } 302 | 303 | channel_impl!( 304 | TIM1, PinC1, PA8, Alternate; 305 | TIM1, PinC1N, PA7, Alternate; 306 | TIM1, PinC1N, PB13, Alternate; 307 | TIM1, PinC2, PA9, Alternate; 308 | TIM1, PinC2N, PB0, Alternate; 309 | TIM1, PinC2N, PB14, Alternate; 310 | TIM1, PinC3, PA10, Alternate; 311 | TIM1, PinC3N, PB1, Alternate; 312 | TIM1, PinC3N, PB15, Alternate; 313 | TIM1, PinC4, PA11, Alternate; 314 | 315 | TIM3, PinC1, PA6, Alternate; 316 | TIM3, PinC2, PA7, Alternate; 317 | 318 | TIM3, PinC1, PB4, Alternate; 319 | TIM3, PinC2, PB5, Alternate; 320 | TIM3, PinC3, PB0, Alternate; 321 | TIM3, PinC4, PB1, Alternate; 322 | 323 | 324 | TIM14, PinC1, PA4, Alternate; 325 | TIM14, PinC1, PA7, Alternate; 326 | TIM14, PinC1, PB1, Alternate; 327 | 328 | TIM16, PinC1, PA6, Alternate; 329 | TIM16, PinC1, PB8, Alternate; 330 | TIM16, PinC1N, PB6, Alternate; 331 | 332 | TIM17, PinC1, PA7, Alternate; 333 | TIM17, PinC1, PB9, Alternate; 334 | ); 335 | 336 | #[cfg(any( 337 | feature = "stm32f030x8", 338 | feature = "stm32f030xc", 339 | feature = "stm32f051", 340 | feature = "stm32f058", 341 | feature = "stm32f070xb", 342 | feature = "stm32f071", 343 | feature = "stm32f072", 344 | feature = "stm32f078", 345 | feature = "stm32f091", 346 | feature = "stm32f098", 347 | ))] 348 | channel_impl!( 349 | TIM15, PinC1, PA2, Alternate; 350 | TIM15, PinC2, PA3, Alternate; 351 | 352 | TIM15, PinC1, PB14, Alternate; 353 | TIM15, PinC2, PB15, Alternate; 354 | ); 355 | 356 | #[cfg(any( 357 | feature = "stm32f030", 358 | feature = "stm32f051", 359 | feature = "stm32f058", 360 | feature = "stm32f070", 361 | feature = "stm32f071", 362 | feature = "stm32f072", 363 | feature = "stm32f078", 364 | feature = "stm32f091", 365 | feature = "stm32f098" 366 | ))] 367 | use crate::gpio::gpioc::*; 368 | 369 | #[cfg(any( 370 | feature = "stm32f030", 371 | feature = "stm32f051", 372 | feature = "stm32f058", 373 | feature = "stm32f070", 374 | feature = "stm32f071", 375 | feature = "stm32f072", 376 | feature = "stm32f078", 377 | feature = "stm32f091", 378 | feature = "stm32f098" 379 | ))] 380 | channel_impl!( 381 | TIM3, PinC1, PC6, Alternate; 382 | TIM3, PinC2, PC7, Alternate; 383 | TIM3, PinC3, PC8, Alternate; 384 | TIM3, PinC4, PC9, Alternate; 385 | ); 386 | 387 | #[cfg(any( 388 | feature = "stm32f071", 389 | feature = "stm32f072", 390 | feature = "stm32f078", 391 | feature = "stm32f091", 392 | feature = "stm32f098" 393 | ))] 394 | use crate::gpio::gpioe::*; 395 | 396 | #[cfg(any( 397 | feature = "stm32f071", 398 | feature = "stm32f072", 399 | feature = "stm32f078", 400 | feature = "stm32f091", 401 | feature = "stm32f098" 402 | ))] 403 | channel_impl!( 404 | TIM1, PinC1, PE9, Alternate; 405 | TIM1, PinC2, PE11, Alternate; 406 | TIM1, PinC3, PE13, Alternate; 407 | TIM1, PinC4, PE14, Alternate; 408 | 409 | TIM3, PinC1, PE3, Alternate; 410 | TIM3, PinC2, PE4, Alternate; 411 | TIM3, PinC3, PE5, Alternate; 412 | TIM3, PinC4, PE6, Alternate; 413 | 414 | TIM16, PinC1, PE0, Alternate; 415 | 416 | TIM17, PinC1, PE1, Alternate; 417 | ); 418 | 419 | #[cfg(any( 420 | feature = "stm32f071", 421 | feature = "stm32f072", 422 | feature = "stm32f078", 423 | feature = "stm32f091", 424 | feature = "stm32f098", 425 | ))] 426 | use crate::gpio::gpiof::*; 427 | 428 | #[cfg(any( 429 | feature = "stm32f071", 430 | feature = "stm32f072", 431 | feature = "stm32f078", 432 | feature = "stm32f091", 433 | feature = "stm32f098", 434 | ))] 435 | channel_impl!( 436 | TIM15, PinC1, PF9, Alternate; 437 | TIM15, PinC2, PF10, Alternate; 438 | ); 439 | -------------------------------------------------------------------------------- /src/tsc.rs: -------------------------------------------------------------------------------- 1 | //! Touch sense controller 2 | //! 3 | //! From STM32 (https://www.st.com/content/ccc/resource/technical/document/application_note/9d/be/03/8c/5d/8c/49/50/DM00088471.pdf/files/DM00088471.pdf/jcr:content/translations/en.DM00088471.pdf): 4 | //! 5 | //! The Cs capacitance is a key parameter for sensitivity. For touchkey sensors, the Cs value is 6 | //! usually comprised between 8.7nF to 22nF. For linear and rotary touch sensors, the value is 7 | //! usually comprised between 47nF and 100nF. These values are given as reference for an 8 | //! electrode fitting a human finger tip size across a few millimeters dielectric panel. 9 | 10 | use crate::gpio::*; 11 | use crate::pac::TSC; 12 | use crate::rcc::Rcc; 13 | 14 | #[derive(Debug)] 15 | pub enum Event { 16 | /// Max count error 17 | MaxCountError, 18 | /// End of acquisition 19 | EndOfAcquisition, 20 | } 21 | 22 | #[derive(Debug)] 23 | pub enum Error { 24 | /// Max count error 25 | MaxCountError, 26 | /// Wrong GPIO for reading 27 | InvalidPin, 28 | } 29 | 30 | pub trait TscPin { 31 | type GROUP; 32 | type OFFSET; 33 | 34 | /// Returns the group a pin belongs to 35 | fn group() -> Self::GROUP; 36 | 37 | /// Returns the offset of the pin within the control registers 38 | fn offset() -> Self::OFFSET; 39 | } 40 | 41 | macro_rules! tsc_pins { 42 | ($($pin:ty => ($group:expr,$offset:expr)),+ $(,)*) => { 43 | $( 44 | impl TscPin for $pin { 45 | type GROUP = u8; 46 | type OFFSET = u8; 47 | 48 | fn group() -> u8 { $group } 49 | fn offset() -> u8 { $offset } 50 | } 51 | )+ 52 | }; 53 | } 54 | 55 | tsc_pins!( 56 | gpioa::PA0> => (1_u8, 1_u8), 57 | gpioa::PA1> => (1_u8, 2_u8), 58 | gpioa::PA2> => (1_u8, 3_u8), 59 | gpioa::PA3> => (1_u8, 4_u8), 60 | ); 61 | 62 | tsc_pins!( 63 | gpioa::PA4> => (2_u8, 1_u8), 64 | gpioa::PA5> => (2_u8, 2_u8), 65 | gpioa::PA6> => (2_u8, 3_u8), 66 | gpioa::PA7> => (2_u8, 4_u8), 67 | ); 68 | 69 | // all with a TSC minus 42 and 48 70 | #[cfg(any( 71 | feature = "stm32f051", 72 | feature = "stm32f058", 73 | feature = "stm32f071", 74 | feature = "stm32f072", 75 | feature = "stm32f078", 76 | feature = "stm32f091", 77 | feature = "stm32f098" 78 | ))] 79 | tsc_pins!( gpioc::PC5> => (3_u8, 1_u8) ); 80 | 81 | tsc_pins!( 82 | gpiob::PB0> => (3_u8, 2_u8), 83 | gpiob::PB1> => (3_u8, 3_u8), 84 | ); 85 | 86 | // all with a TCS minus 58, 78 and 98 87 | #[cfg(any( 88 | feature = "stm32f042", 89 | feature = "stm32f048", 90 | feature = "stm32f051", 91 | feature = "stm32f071", 92 | feature = "stm32f072", 93 | feature = "stm32f091" 94 | ))] 95 | tsc_pins!( gpiob::PB2> => (3_u8, 4_u8) ); 96 | 97 | tsc_pins!( 98 | gpioa::PA9> => (4_u8, 1_u8), 99 | gpioa::PA10> => (4_u8, 2_u8), 100 | gpioa::PA11> => (4_u8, 3_u8), 101 | gpioa::PA12> => (4_u8, 4_u8), 102 | ); 103 | 104 | tsc_pins!( 105 | gpiob::PB3> => (5_u8, 1_u8), 106 | gpiob::PB4> => (5_u8, 2_u8), 107 | gpiob::PB6> => (5_u8, 3_u8), 108 | gpiob::PB7> => (5_u8, 4_u8), 109 | ); 110 | 111 | // all with a TSC minus 42 and 48 112 | #[cfg(any( 113 | feature = "stm32f051", 114 | feature = "stm32f058", 115 | feature = "stm32f071", 116 | feature = "stm32f072", 117 | feature = "stm32f078", 118 | feature = "stm32f091", 119 | feature = "stm32f098" 120 | ))] 121 | tsc_pins!( 122 | gpiob::PB11> => (6_u8, 1_u8), 123 | gpiob::PB12> => (6_u8, 2_u8), 124 | gpiob::PB13> => (6_u8, 3_u8), 125 | gpiob::PB14> => (6_u8, 4_u8), 126 | ); 127 | 128 | // all with a TSC and gpioe 129 | #[cfg(any( 130 | feature = "stm32f071", 131 | feature = "stm32f072", 132 | feature = "stm32f078", 133 | feature = "stm32f091", 134 | feature = "stm32f098" 135 | ))] 136 | tsc_pins!( 137 | gpioe::PE2> => (7_u8, 1_u8), 138 | gpioe::PE3> => (7_u8, 2_u8), 139 | gpioe::PE4> => (7_u8, 3_u8), 140 | gpioe::PE5> => (7_u8, 4_u8), 141 | ); 142 | 143 | // all with a TSC and gpiod 144 | #[cfg(any( 145 | feature = "stm32f071", 146 | feature = "stm32f072", 147 | feature = "stm32f078", 148 | feature = "stm32f091", 149 | feature = "stm32f098" 150 | ))] 151 | tsc_pins!( 152 | gpiod::PD12> => (8_u8, 1_u8), 153 | gpiod::PD13> => (8_u8, 2_u8), 154 | gpiod::PD14> => (8_u8, 3_u8), 155 | gpiod::PD15> => (8_u8, 4_u8), 156 | ); 157 | 158 | pub struct Tsc { 159 | tsc: TSC, 160 | } 161 | 162 | #[derive(Debug)] 163 | pub struct Config { 164 | pub clock_prescale: Option, 165 | pub max_count: Option, 166 | pub charge_transfer_high: Option, 167 | pub charge_transfer_low: Option, 168 | } 169 | 170 | #[derive(Debug)] 171 | pub enum ClockPrescaler { 172 | Hclk = 0b000, 173 | HclkDiv2 = 0b001, 174 | HclkDiv4 = 0b010, 175 | HclkDiv8 = 0b011, 176 | HclkDiv16 = 0b100, 177 | HclkDiv32 = 0b101, 178 | HclkDiv64 = 0b110, 179 | HclkDiv128 = 0b111, 180 | } 181 | 182 | #[derive(Debug)] 183 | pub enum MaxCount { 184 | /// 000: 255 185 | U255 = 0b000, 186 | /// 001: 511 187 | U511 = 0b001, 188 | /// 010: 1023 189 | U1023 = 0b010, 190 | /// 011: 2047 191 | U2047 = 0b011, 192 | /// 100: 4095 193 | U4095 = 0b100, 194 | /// 101: 8191 195 | U8191 = 0b101, 196 | /// 110: 16383 197 | U16383 = 0b110, 198 | } 199 | 200 | #[derive(Debug)] 201 | /// How many tsc cycles are spent charging / discharging 202 | pub enum ChargeDischargeTime { 203 | C1 = 0b0000, 204 | C2 = 0b0001, 205 | C3 = 0b0010, 206 | C4 = 0b0011, 207 | C5 = 0b0100, 208 | C6 = 0b0101, 209 | C7 = 0b0110, 210 | C8 = 0b0111, 211 | C9 = 0b1000, 212 | C10 = 0b1001, 213 | C11 = 0b1010, 214 | C12 = 0b1011, 215 | C13 = 0b1100, 216 | C14 = 0b1101, 217 | C15 = 0b1110, 218 | C16 = 0b1111, 219 | } 220 | 221 | impl Tsc { 222 | /// Initialise the touch controller peripheral 223 | pub fn tsc(tsc: TSC, rcc: &mut Rcc, cfg: Option) -> Self { 224 | // Enable the peripheral clock 225 | rcc.regs.ahbenr.modify(|_, w| w.tscen().set_bit()); 226 | rcc.regs.ahbrstr.modify(|_, w| w.tscrst().set_bit()); 227 | rcc.regs.ahbrstr.modify(|_, w| w.tscrst().clear_bit()); 228 | 229 | let config = cfg.unwrap_or(Config { 230 | clock_prescale: None, 231 | max_count: None, 232 | charge_transfer_high: None, 233 | charge_transfer_low: None, 234 | }); 235 | 236 | tsc.cr.write(|w| unsafe { 237 | w.ctph() 238 | .bits( 239 | config 240 | .charge_transfer_high 241 | .unwrap_or(ChargeDischargeTime::C2) as u8, 242 | ) 243 | .ctpl() 244 | .bits( 245 | config 246 | .charge_transfer_low 247 | .unwrap_or(ChargeDischargeTime::C2) as u8, 248 | ) 249 | .sse() 250 | .set_bit() 251 | .ssd() 252 | .bits(16) 253 | .pgpsc() 254 | .bits(config.clock_prescale.unwrap_or(ClockPrescaler::HclkDiv16) as u8) 255 | .mcv() 256 | .bits(config.max_count.unwrap_or(MaxCount::U8191) as u8) 257 | .tsce() 258 | .set_bit() 259 | }); 260 | 261 | // clear interrupt & flags 262 | tsc.icr.write(|w| w.eoaic().set_bit().mceic().set_bit()); 263 | 264 | Tsc { tsc } 265 | } 266 | 267 | /// Set up sample group 268 | pub fn setup_sample_group(&mut self, _: &mut PIN) 269 | where 270 | PIN: TscPin, 271 | { 272 | let bit_pos = PIN::offset() - 1 + (4 * (PIN::group() - 1)); 273 | let group_pos = PIN::group() - 1; 274 | 275 | // Schmitt trigger hysteresis on sample IOs 276 | self.tsc 277 | .iohcr 278 | .modify(|r, w| unsafe { w.bits(r.bits() | 1 << bit_pos) }); 279 | 280 | // Set the sampling pin 281 | self.tsc 282 | .ioscr 283 | .modify(|r, w| unsafe { w.bits(r.bits() | 1 << bit_pos) }); 284 | 285 | // Set the acquisition group based on the channel pins 286 | self.tsc 287 | .iogcsr 288 | .modify(|r, w| unsafe { w.bits(r.bits() | 1 << group_pos) }); 289 | } 290 | 291 | /// Add a GPIO for use as a channel 292 | pub fn enable_channel(&self, _channel: &mut PIN) 293 | where 294 | PIN: TscPin, 295 | { 296 | let bit_pos = PIN::offset() - 1 + (4 * (PIN::group() - 1)); 297 | 298 | // Set a channel pin 299 | self.tsc 300 | .ioccr 301 | .modify(|r, w| unsafe { w.bits(r.bits() | 1 << bit_pos) }); 302 | } 303 | 304 | /// Remove a GPIO from use as a channel 305 | pub fn disable_channel(&self, _channel: &mut PIN) 306 | where 307 | PIN: TscPin, 308 | { 309 | let bit_pos = PIN::offset() - 1 + (4 * (PIN::group() - 1)); 310 | 311 | // Remove a channel pin 312 | self.tsc 313 | .ioccr 314 | .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << bit_pos)) }); 315 | } 316 | 317 | /// Starts a charge acquisition 318 | pub fn start(&self) { 319 | self.clear(Event::EndOfAcquisition); 320 | self.clear(Event::MaxCountError); 321 | 322 | // Discharge the caps ready for a new reading 323 | self.tsc.cr.modify(|_, w| w.iodef().clear_bit()); 324 | self.tsc.cr.modify(|_, w| w.start().set_bit()); 325 | } 326 | 327 | /// Check for events on the TSC 328 | pub fn check_event(&self) -> Option { 329 | let isr = self.tsc.isr.read(); 330 | if isr.eoaf().bit_is_set() { 331 | Some(Event::EndOfAcquisition) 332 | } else if isr.mcef().bit_is_set() { 333 | Some(Event::MaxCountError) 334 | } else { 335 | None 336 | } 337 | } 338 | 339 | /// Clear interrupt & flags 340 | pub fn clear(&self, event: Event) { 341 | match event { 342 | Event::EndOfAcquisition => { 343 | self.tsc.icr.write(|w| w.eoaic().set_bit()); 344 | } 345 | Event::MaxCountError => { 346 | self.tsc.icr.write(|w| w.mceic().set_bit()); 347 | } 348 | } 349 | } 350 | 351 | /// Blocks waiting for a acquisition to complete or for a Max Count Error 352 | pub fn acquire(&self) -> Result<(), Error> { 353 | // Start the acquisition 354 | self.start(); 355 | 356 | loop { 357 | match self.check_event() { 358 | Some(Event::MaxCountError) => { 359 | self.clear(Event::MaxCountError); 360 | break Err(Error::MaxCountError); 361 | } 362 | Some(Event::EndOfAcquisition) => { 363 | self.clear(Event::EndOfAcquisition); 364 | break Ok(()); 365 | } 366 | None => {} 367 | } 368 | } 369 | } 370 | 371 | /// Reads the group count register 372 | pub fn read(&self, _input: &mut PIN) -> Result 373 | where 374 | PIN: TscPin, 375 | { 376 | let bit_pos = PIN::offset() - 1 + (4 * (PIN::group() - 1)); 377 | 378 | // Read the current channel config 379 | let channel = self.tsc.ioccr.read().bits(); 380 | 381 | // Check whether one of the enabled pins was supplied 382 | if channel & (1 << bit_pos) != 0 { 383 | Ok(self.read_unchecked(PIN::group())) 384 | } else { 385 | Err(Error::InvalidPin) 386 | } 387 | } 388 | 389 | /// Reads the tsc group count register 390 | pub fn read_unchecked(&self, group: u8) -> u16 { 391 | match group { 392 | 1 => self.tsc.iog1cr().read().cnt().bits(), 393 | 2 => self.tsc.iog2cr().read().cnt().bits(), 394 | 3 => self.tsc.iog3cr().read().cnt().bits(), 395 | 4 => self.tsc.iog4cr().read().cnt().bits(), 396 | 5 => self.tsc.iog5cr().read().cnt().bits(), 397 | 6 => self.tsc.iog6cr().read().cnt().bits(), 398 | _ => 0, 399 | } 400 | } 401 | 402 | /// Enables an interrupt event 403 | pub fn listen(&mut self, event: Event) { 404 | match event { 405 | Event::EndOfAcquisition => { 406 | self.tsc.ier.modify(|_, w| w.eoaie().set_bit()); 407 | } 408 | Event::MaxCountError => { 409 | self.tsc.ier.modify(|_, w| w.mceie().set_bit()); 410 | } 411 | } 412 | } 413 | 414 | /// Disables an interrupt event 415 | pub fn unlisten(&self, event: Event) { 416 | match event { 417 | Event::EndOfAcquisition => { 418 | self.tsc.ier.modify(|_, w| w.eoaie().clear_bit()); 419 | } 420 | Event::MaxCountError => { 421 | self.tsc.ier.modify(|_, w| w.mceie().clear_bit()); 422 | } 423 | } 424 | } 425 | 426 | /// Releases the TSC peripheral 427 | pub fn free(self) -> TSC { 428 | self.tsc 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /src/usb.rs: -------------------------------------------------------------------------------- 1 | //! USB peripheral 2 | //! 3 | //! Requires the `stm32-usbd` feature. 4 | //! 5 | //! See 6 | //! for usage examples. 7 | 8 | use crate::pac::{RCC, SYSCFG, USB}; 9 | use stm32_usbd::UsbPeripheral; 10 | 11 | use crate::gpio::gpioa::{PA11, PA12}; 12 | use crate::gpio::{Floating, Input}; 13 | pub use stm32_usbd::UsbBus; 14 | 15 | pub struct Peripheral { 16 | pub usb: USB, 17 | pub pin_dm: PA11>, 18 | pub pin_dp: PA12>, 19 | } 20 | 21 | unsafe impl Sync for Peripheral {} 22 | 23 | unsafe impl UsbPeripheral for Peripheral { 24 | const REGISTERS: *const () = USB::ptr() as *const (); 25 | const DP_PULL_UP_FEATURE: bool = true; 26 | const EP_MEMORY: *const () = 0x4000_6000 as _; 27 | const EP_MEMORY_SIZE: usize = 1024; 28 | const EP_MEMORY_ACCESS_2X16: bool = true; 29 | 30 | fn enable() { 31 | let rcc = unsafe { &*RCC::ptr() }; 32 | 33 | cortex_m::interrupt::free(|_| { 34 | // Enable USB peripheral 35 | rcc.apb1enr.modify(|_, w| w.usben().set_bit()); 36 | 37 | // Reset USB peripheral 38 | rcc.apb1rstr.modify(|_, w| w.usbrst().set_bit()); 39 | rcc.apb1rstr.modify(|_, w| w.usbrst().clear_bit()); 40 | }); 41 | } 42 | 43 | fn startup_delay() { 44 | // There is a chip specific startup delay. For STM32F103xx it's 1µs and this should wait for 45 | // at least that long. 46 | cortex_m::asm::delay(72); 47 | } 48 | } 49 | 50 | /// Remap PA11/PA12 pins to PA09/PA10 for USB on 51 | /// TSSOP20 (STM32F042F) or UFQFPN28 (STM32F042G) packages 52 | pub fn remap_pins(rcc: &mut RCC, syscfg: &mut SYSCFG) { 53 | rcc.apb2enr.modify(|_, w| w.syscfgen().set_bit()); 54 | syscfg.cfgr1.modify(|_, w| w.pa11_pa12_rmp().remapped()); 55 | } 56 | 57 | pub type UsbBusType = UsbBus; 58 | -------------------------------------------------------------------------------- /src/watchdog.rs: -------------------------------------------------------------------------------- 1 | //! API for the IWDG 2 | //! 3 | //! You can activate the watchdog by calling `start` or the setting appropriate 4 | //! device option bit when programming. 5 | //! 6 | //! After activating the watchdog, you'll have to regularly `feed` the watchdog. 7 | //! If more time than `timeout` has gone by since the last `feed`, your 8 | //! microcontroller will be reset. 9 | //! 10 | //! This is useful if you fear that your program may get stuck. In that case it 11 | //! won't feed the watchdog anymore, the watchdog will reset the microcontroller 12 | //! and thus your program will function again. 13 | //! 14 | //! **Attention**: 15 | //! 16 | //! The IWDG runs on a separate 40kHz low-accuracy clock (30kHz-60kHz). You may 17 | //! want to some buffer in your interval. 18 | //! 19 | //! Per default the iwdg continues to run even when you stopped execution of code via a debugger. 20 | //! You may want to disable the watchdog when the cpu is stopped 21 | //! 22 | //! ``` ignore 23 | //! let dbgmcu = p.DBGMCU; 24 | //! dbgmcu.apb1_fz.modify(|_, w| w.dbg_iwdg_stop().set_bit()); 25 | //! ``` 26 | //! 27 | //! # Example 28 | //! ``` no_run 29 | //! use stm32f0xx_hal as hal; 30 | //! 31 | //! use crate::hal::pac; 32 | //! use crate::hal::prelude::*; 33 | //! use crate::hal:watchdog::Watchdog; 34 | //! use crate::hal:time::Hertz; 35 | //! 36 | //! let mut p = pac::Peripherals::take().unwrap(); 37 | //! 38 | //! let mut iwdg = Watchdog::new(p.iwdg); 39 | //! iwdg.start(Hertz(100)); 40 | //! loop {} 41 | //! // Whoops, got stuck, the watchdog issues a reset after 10 ms 42 | //! iwdg.feed(); 43 | //! ``` 44 | use embedded_hal::watchdog; 45 | 46 | use crate::pac::IWDG; 47 | use crate::time::Hertz; 48 | 49 | /// Watchdog instance 50 | pub struct Watchdog { 51 | iwdg: IWDG, 52 | } 53 | 54 | impl watchdog::Watchdog for Watchdog { 55 | /// Feed the watchdog, so that at least one `period` goes by before the next 56 | /// reset 57 | fn feed(&mut self) { 58 | self.iwdg.kr.write(|w| w.key().reset()); 59 | } 60 | } 61 | 62 | /// Timeout configuration for the IWDG 63 | #[derive(PartialEq, PartialOrd, Clone, Copy)] 64 | pub struct IwdgTimeout { 65 | psc: u8, 66 | reload: u16, 67 | } 68 | 69 | impl From for IwdgTimeout { 70 | /// This converts the value so it's usable by the IWDG 71 | /// Due to conversion losses, the specified frequency is a maximum 72 | /// 73 | /// It can also only represent values < 10000 Hertz 74 | fn from(hz: Hertz) -> Self { 75 | let mut time = 40_000 / 4 / hz.0; 76 | let mut psc = 0; 77 | let mut reload = 0; 78 | while psc < 7 { 79 | reload = time; 80 | if reload < 0x1000 { 81 | break; 82 | } 83 | psc += 1; 84 | time /= 2; 85 | } 86 | // As we get an integer value, reload is always below 0xFFF 87 | let reload = reload as u16; 88 | IwdgTimeout { psc, reload } 89 | } 90 | } 91 | 92 | impl Watchdog { 93 | pub fn new(iwdg: IWDG) -> Self { 94 | Self { iwdg } 95 | } 96 | } 97 | 98 | impl watchdog::WatchdogEnable for Watchdog { 99 | type Time = IwdgTimeout; 100 | fn start(&mut self, period: T) 101 | where 102 | T: Into, 103 | { 104 | let time: IwdgTimeout = period.into(); 105 | // Feed the watchdog in case it's already running 106 | // (Waiting for the registers to update takes sometime) 107 | self.iwdg.kr.write(|w| w.key().reset()); 108 | // Enable the watchdog 109 | self.iwdg.kr.write(|w| w.key().start()); 110 | self.iwdg.kr.write(|w| w.key().enable()); 111 | // Wait until it's safe to write to the registers 112 | while self.iwdg.sr.read().pvu().bit() {} 113 | self.iwdg.pr.write(|w| w.pr().bits(time.psc)); 114 | while self.iwdg.sr.read().rvu().bit() {} 115 | self.iwdg.rlr.write(|w| w.rl().bits(time.reload)); 116 | // Wait until the registers are updated before issuing a reset with 117 | // (potentially false) values 118 | while self.iwdg.sr.read().bits() != 0 {} 119 | self.iwdg.kr.write(|w| w.key().reset()); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /tools/capture_example_bloat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | version="stable" 4 | features="stm32f042,rt" 5 | 6 | filename="bloat_log_"$version"_"`date -Iminutes`".txt" 7 | filenamenoopt="bloat_noopt_log_"$version"_"`date -Iminutes`".txt" 8 | 9 | cargo +$version rustc -- -V >>$filename 10 | cargo +$version rustc -- -V >>$filenamenoopt 11 | 12 | for i in `find examples -name "*.rs"`; do 13 | name=$(echo $i | sed -e "s,examples/,,g" -e "s,\.rs,,g") 14 | echo "Processing example $name" 15 | 16 | echo >>$filename 17 | echo "Bloat for example $name" >>$filename 18 | cargo +$version bloat --release --example $name --features="$features" -n 60 >>$filename 19 | echo >>$filename 20 | echo "Section sizes: " >>$filename 21 | cargo +$version size --release --example $name --features="$features" >>$filename 22 | 23 | echo >>$filenamenoopt 24 | echo "Bloat for example $name" >>$filenamenoopt 25 | cargo +$version bloat --example $name --features="$features" -n 60 >>$filenamenoopt 26 | echo >>$filenamenoopt 27 | echo "Section size: " >>$filenamenoopt 28 | cargo +$version size --example $name --features="$features" >>$filenamenoopt 29 | done 30 | 31 | echo "Captured bloat for rustc version \"$version\" for all examples with features \"$features\" into $filename and $filenamenoopt" 32 | -------------------------------------------------------------------------------- /tools/capture_nightly_example_bloat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | version="nightly" 4 | features="stm32f042,rt" 5 | 6 | filename="bloat_log_"$version"_"`date -Iminutes`".txt" 7 | filenamenoopt="bloat_noopt_log_"$version"_"`date -Iminutes`".txt" 8 | 9 | cargo +$version rustc -- -V >>$filename 10 | cargo +$version rustc -- -V >>$filenamenoopt 11 | 12 | for i in `find examples -name "*.rs"`; do 13 | name=$(echo $i | sed -e "s,examples/,,g" -e "s,\.rs,,g") 14 | echo "Processing example $name" 15 | 16 | echo >>$filename 17 | echo "Bloat for example $name" >>$filename 18 | cargo +$version bloat --release --example $name --features="$features" -n 60 >>$filename 19 | echo >>$filename 20 | echo "Section sizes: " >>$filename 21 | cargo +$version size --release --example $name --features="$features" >>$filename 22 | 23 | echo >>$filenamenoopt 24 | echo "Bloat for example $name" >>$filenamenoopt 25 | cargo +$version bloat --example $name --features="$features" -n 60 >>$filenamenoopt 26 | echo >>$filenamenoopt 27 | echo "Section size: " >>$filenamenoopt 28 | cargo +$version size --example $name --features="$features" >>$filenamenoopt 29 | done 30 | 31 | echo "Captured bloat for rustc version \"$version\" for all examples with features \"$features\" into $filename and $filenamenoopt" 32 | -------------------------------------------------------------------------------- /tools/check.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import json 4 | import subprocess 5 | import sys 6 | 7 | 8 | def run_inner(args): 9 | print("Running `{}`...".format(" ".join(args))) 10 | ret = subprocess.call(args) == 0 11 | print("") 12 | return ret 13 | 14 | 15 | def run(mcu, cargo_cmd): 16 | if mcu == "": 17 | return run_inner(cargo_cmd) 18 | else: 19 | return run_inner(cargo_cmd + ["--examples", "--features={}".format(mcu)]) 20 | 21 | 22 | def main(): 23 | cargo_meta = json.loads( 24 | subprocess.check_output("cargo metadata --no-deps --format-version=1", 25 | shell=True, 26 | universal_newlines=True) 27 | ) 28 | 29 | crate_info = cargo_meta["packages"][0] 30 | 31 | features = [ 32 | "{},rt,stm32-usbd".format(x) 33 | for x in crate_info["features"].keys() 34 | if x != "device-selected" 35 | and x != "rt" 36 | and x != "stm32f030" 37 | and x != "stm32-usbd" 38 | ] 39 | 40 | if 'size_check' in sys.argv: 41 | cargo_cmd = ['cargo', 'build', '--release'] 42 | else: 43 | cargo_cmd = ['cargo', 'check'] 44 | 45 | if not all(map(lambda f: run(f, cargo_cmd), 46 | features)): 47 | sys.exit(-1) 48 | 49 | 50 | if __name__ == "__main__": 51 | main() 52 | 53 | --------------------------------------------------------------------------------