├── .cargo └── config.toml ├── .gdbinit ├── .gitattributes ├── .github ├── pull_request_template.md └── workflows │ ├── changelog.yml │ ├── ci.yml │ ├── clippy.yml │ └── rustfmt.yml ├── .gitignore ├── .vscode └── settings.json ├── .zed └── settings.json ├── BluePillPinout.jpg ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.rs ├── examples ├── adc-dma-circ.rs ├── adc-dma-rx.rs ├── adc.rs ├── adc_temperature.rs ├── blinky.rs ├── blinky_generic.rs ├── blinky_rtc.rs ├── blinky_rtcalarm_irq.rs ├── blinky_timer_irq.rs ├── can-echo.rs ├── can-loopback.rs ├── can-rtic.rs ├── crc.rs ├── delay-timer-blinky.rs ├── delay.rs ├── dynamic_gpio.rs ├── enc28j60-coap.rs.disabled ├── enc28j60.rs.disabled ├── exti.rs ├── exti_rtic.rs ├── gpio_input.rs ├── hello.rs ├── i2c-bme280 │ ├── Cargo.toml │ ├── memory.x │ └── src │ │ └── main.rs ├── itm.rs ├── led.rs ├── mfrc522.rs ├── motor.rs.disabled ├── mpu9250.rs ├── multi_mode_gpio.rs ├── nojtag.rs ├── panics.rs ├── pwm.rs ├── pwm_custom.rs ├── pwm_input.rs ├── qei.rs ├── rtc.rs ├── serial-dma-circ.rs ├── serial-dma-peek.rs ├── serial-dma-rx.rs ├── serial-dma-tx.rs ├── serial-fmt.rs ├── serial-interrupt-idle.rs ├── serial.rs ├── serial_9bits.rs ├── serial_config.rs ├── serial_reconfigure.rs ├── spi-dma.rs ├── spi-slave.rs ├── spi.rs ├── timer-interrupt-rtic.rs ├── usb_serial.rs ├── usb_serial_interrupt.rs └── usb_serial_rtic.rs ├── memory.x ├── src ├── adc.rs ├── afio.rs ├── backup_domain.rs ├── bb.rs ├── can.rs ├── crc.rs ├── dac.rs ├── dma.rs ├── flash.rs ├── gpio.rs ├── gpio │ ├── erased.rs │ ├── hal_02.rs │ ├── hal_1.rs │ └── partially_erased.rs ├── i2c.rs ├── i2c │ ├── blocking.rs │ ├── hal_02.rs │ └── hal_1.rs ├── lib.rs ├── pacext.rs ├── pacext │ ├── adc.rs │ └── uart.rs ├── prelude.rs ├── rcc.rs ├── rcc │ └── enable.rs ├── rtc.rs ├── serial.rs ├── serial │ ├── hal_02.rs │ └── hal_1.rs ├── spi.rs ├── spi │ ├── hal_02.rs │ └── hal_1.rs ├── time.rs ├── timer.rs ├── timer │ ├── counter.rs │ ├── delay.rs │ ├── hal_02.rs │ ├── hal_1.rs │ ├── monotonic.rs │ ├── pins.rs │ ├── pwm.rs │ └── pwm_input.rs ├── usb.rs └── watchdog.rs └── tools └── check.py /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.thumbv7m-none-eabi] 2 | runner = 'probe-rs run --chip STM32F103C8Tx' 3 | rustflags = [ 4 | "-C", "link-arg=-Tlink.x", 5 | #"-C", "link-arg=-Tdefmt.x", 6 | ] 7 | 8 | [build] 9 | target = "thumbv7m-none-eabi" 10 | 11 | [env] 12 | DEFMT_LOG = "info" 13 | -------------------------------------------------------------------------------- /.gdbinit: -------------------------------------------------------------------------------- 1 | target remote :3333 2 | 3 | monitor arm semihosting enable 4 | 5 | # # send captured ITM to the file itm.fifo 6 | # # (the microcontroller SWO pin must be connected to the programmer SWO pin) 7 | # # 8000000 must match the core clock frequency 8 | # monitor tpiu config internal itm.fifo uart off 8000000 9 | 10 | # # OR: make the microcontroller SWO pin output compatible with UART (8N1) 11 | # # 2000000 is the frequency of the SWO pin 12 | # monitor tpiu config external uart off 8000000 2000000 13 | 14 | # # enable ITM port 0 15 | # monitor itm port 0 on 16 | 17 | load 18 | step 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | CHANGELOG.md merge=union 2 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Thanks for contributing to the project! 2 | 3 | Please write a short description of your changes. 4 | 5 | We also maintain a changelog in CHANGELOG.md. If you add a feature or fix a bug, please add an entry there :) 6 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request_target: 3 | 4 | name: Changelog check 5 | 6 | jobs: 7 | changelog: 8 | name: Changelog check 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout sources 12 | uses: actions/checkout@v3 13 | 14 | - name: Changelog updated 15 | uses: Zomzog/changelog-checker@v1.2.0 16 | with: 17 | fileName: CHANGELOG.md 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: master 4 | pull_request: 5 | merge_group: 6 | 7 | name: Continuous integration 8 | 9 | jobs: 10 | ci: 11 | name: CI 12 | runs-on: ubuntu-latest 13 | needs: [check] 14 | if: always() 15 | steps: 16 | - name: Done 17 | run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' 18 | 19 | check: 20 | runs-on: ubuntu-latest 21 | strategy: 22 | matrix: 23 | mcu: 24 | - stm32f100 25 | - stm32f101 26 | - stm32f103 27 | - stm32f105 28 | - stm32f107 29 | rust: 30 | - stable 31 | include: 32 | - rust: nightly 33 | mcu: stm32f103 34 | experimental: true 35 | 36 | steps: 37 | - uses: actions/checkout@v3 38 | - name: Use the latest ${{ matrix.rust }} rustc 39 | run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} 40 | - name: Add Cortex-M3 target 41 | run: rustup target add thumbv7m-none-eabi 42 | 43 | - uses: actions-rs/cargo@v1 44 | with: 45 | command: check 46 | args: --features=${{ matrix.mcu }},rtic,high --examples 47 | 48 | - uses: actions-rs/cargo@v1 49 | with: 50 | command: test 51 | args: --features=${{ matrix.mcu }} --target x86_64-unknown-linux-gnu --lib 52 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 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@v3 12 | - name: Use the latest stable rustc 13 | run: rustup update stable && rustup default stable 14 | - name: Add Cortex-M3 target 15 | run: rustup target add thumbv7m-none-eabi 16 | 17 | - uses: actions-rs/clippy-check@v1 18 | with: 19 | token: ${{ secrets.GITHUB_TOKEN }} 20 | args: --features=stm32f103 --target thumbv7m-none-eabi 21 | -------------------------------------------------------------------------------- /.github/workflows/rustfmt.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: master 4 | pull_request: 5 | merge_group: 6 | 7 | name: Code formatting check 8 | 9 | jobs: 10 | fmt: 11 | name: Rustfmt 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Use the latest stable rustc 16 | run: rustup update stable && rustup default stable 17 | 18 | - uses: actions-rs/cargo@v1 19 | with: 20 | command: fmt 21 | args: --all -- --check 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | *.org 3 | .#* 4 | .gdb_history 5 | Cargo.lock 6 | target/ 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust.all_targets": false, 3 | "rust.target": "thumbv7m-none-eabi", 4 | "rust.all_features": false, 5 | "rust.features": [ 6 | "rtic", 7 | "stm32f103", 8 | "medium" 9 | ], 10 | "rust-analyzer.checkOnSave.allTargets": false, 11 | "rust-analyzer.checkOnSave.extraArgs": [ 12 | "--target", 13 | "thumbv7m-none-eabi" 14 | ], 15 | "rust-analyzer.cargo.features": [ 16 | "rtic", 17 | "stm32f103", 18 | "medium" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.zed/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "lsp": { 3 | "rust-analyzer": { 4 | "initialization_options": { 5 | "cargo": { 6 | "features": ["defmt", "rtic", "stm32f103", "medium"] 7 | }, 8 | "check": { 9 | "allTargets": false, 10 | "targets": "thumbv7em-none-eabi" 11 | } 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /BluePillPinout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stm32-rs/stm32f1xx-hal/e78625e1000bfecb901be81f9dda5f9870e2b6ee/BluePillPinout.jpg -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | rust-version = "1.59" 4 | 5 | authors = [ 6 | "Jorge Aparicio ", 7 | "Daniel Egger ", 8 | ] 9 | categories = ["embedded", "hardware-support", "no-std"] 10 | description = "HAL for the STM32F1xx family of microcontrollers" 11 | keywords = ["arm", "cortex-m", "stm32", "hal"] 12 | license = "MIT OR Apache-2.0" 13 | name = "stm32f1xx-hal" 14 | repository = "https://github.com/stm32-rs/stm32f1xx-hal" 15 | documentation = "https://docs.rs/stm32f1xx-hal" 16 | readme = "README.md" 17 | version = "0.10.0" 18 | 19 | [package.metadata.docs.rs] 20 | features = ["stm32f103", "rtic", "high"] 21 | default-target = "x86_64-unknown-linux-gnu" 22 | 23 | [dependencies] 24 | defmt = { version = "1.0", optional = true } 25 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 26 | cortex-m-rt = "0.7.3" 27 | nb = "1.1" 28 | embedded-dma = "0.2.0" 29 | bxcan = "0.8.0" 30 | void = { default-features = false, version = "1.0.2" } 31 | fugit = "0.3.7" 32 | fugit-timer = "0.1.3" 33 | rtic-monotonic = { version = "1.0", optional = true } 34 | bitflags = "1.3.2" 35 | vcell = "0.1.3" 36 | 37 | [dependencies.stm32f1] 38 | version = "0.16.0" 39 | features = ["atomics"] 40 | 41 | [dependencies.embedded-hal-02] 42 | package = "embedded-hal" 43 | version = "0.2.7" 44 | features = ["unproven"] 45 | 46 | [dependencies.embedded-hal] 47 | version = "1.0" 48 | 49 | [dependencies.embedded-hal-nb] 50 | version = "1.0" 51 | 52 | [dependencies.embedded-io] 53 | version = "0.6.1" 54 | 55 | [dependencies.stm32-usbd] 56 | version = "0.7.0" 57 | optional = true 58 | 59 | [dev-dependencies] 60 | panic-halt = "1.0.0" 61 | panic-semihosting = "0.6.0" 62 | panic-itm = "0.4.2" 63 | cortex-m-rtic = "1.1.3" 64 | cortex-m-semihosting = "0.5.0" 65 | heapless = "0.8.0" 66 | mfrc522 = { version = "0.7.0", features = ["eh02"] } 67 | mpu9250 = "0.25.0" 68 | usb-device = "0.3.2" 69 | usbd-serial = "0.2.2" 70 | 71 | [features] 72 | doc = [] 73 | stm32f100 = ["stm32f1/stm32f100"] 74 | stm32f101 = ["stm32f1/stm32f101"] 75 | stm32f103 = ["stm32f1/stm32f103", "has-can", "stm32-usbd"] 76 | stm32f105 = ["stm32f1/stm32f107", "connectivity"] 77 | stm32f107 = ["stm32f1/stm32f107", "connectivity"] 78 | 79 | defmt = ["dep:defmt", "stm32f1/defmt"] 80 | 81 | # Devices with 64 or 128 Kb ROM 82 | medium = [] 83 | # Devices with 256 or 512 Kb ROM 84 | high = ["medium", "has-dac"] 85 | # Devices with 768 Kb ROM or more 86 | xl = ["high"] 87 | # Connectivity line devices (`stm32f105xx` and `stm32f107xx`) 88 | connectivity = ["medium", "has-can", "has-dac"] 89 | # Devices with CAN interface 90 | has-can = [] 91 | # Devices with Dac 92 | has-dac = [] 93 | 94 | rtic = ["rtic-monotonic"] 95 | 96 | [profile.dev] 97 | incremental = false 98 | codegen-units = 1 99 | 100 | [profile.release] 101 | codegen-units = 1 102 | debug = true 103 | lto = true 104 | 105 | [[example]] 106 | name = "usb_serial" 107 | required-features = ["stm32f103", "stm32-usbd"] 108 | 109 | [[example]] 110 | name = "usb_serial_interrupt" 111 | required-features = ["stm32f103", "stm32-usbd"] 112 | 113 | [[example]] 114 | name = "usb_serial_rtic" 115 | required-features = ["stm32f103", "stm32-usbd"] 116 | 117 | [[example]] 118 | name = "blinky_timer_irq" 119 | required-features = [] 120 | 121 | [[example]] 122 | name = "blinky_rtcalarm_irq" 123 | required-features = [] 124 | 125 | [[example]] 126 | name = "qei" 127 | required-features = ["medium"] 128 | 129 | [[example]] 130 | name = "timer-interrupt-rtic" 131 | required-features = ["stm32f103", "medium"] 132 | 133 | [[example]] 134 | name = "exti" 135 | required-features = [] 136 | 137 | [[example]] 138 | name = "can-echo" 139 | required-features = ["has-can"] 140 | 141 | [[example]] 142 | name = "can-loopback" 143 | required-features = ["has-can"] 144 | 145 | [[example]] 146 | name = "can-rtic" 147 | required-features = ["has-can"] 148 | 149 | [[example]] 150 | name = "gpio_input" 151 | required-features = ["stm32f103"] 152 | 153 | [[example]] 154 | name = "serial-interrupt-idle" 155 | required-features = ["stm32f103", "medium"] 156 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2018 Jorge Aparicio 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | #[derive(Clone, Copy, Debug)] 4 | enum GetOneError { 5 | None, 6 | Multiple, 7 | } 8 | 9 | trait IteratorExt: Iterator { 10 | fn get_one(self) -> Result; 11 | } 12 | 13 | impl IteratorExt for T { 14 | fn get_one(mut self) -> Result { 15 | match (self.next(), self.next()) { 16 | (Some(res), None) => Ok(res), 17 | (None, _) => Err(GetOneError::None), 18 | _ => Err(GetOneError::Multiple), 19 | } 20 | } 21 | } 22 | 23 | fn main() { 24 | let _chip_name = match env::vars() 25 | .map(|(a, _)| a) 26 | .filter(|x| x.starts_with("CARGO_FEATURE_STM32F1")) 27 | .get_one() 28 | { 29 | Ok(x) => x, 30 | Err(GetOneError::None) => panic!("No stm32xx Cargo feature enabled"), 31 | Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"), 32 | } 33 | .strip_prefix("CARGO_FEATURE_") 34 | .unwrap() 35 | .to_ascii_lowercase(); 36 | } 37 | -------------------------------------------------------------------------------- /examples/adc-dma-circ.rs: -------------------------------------------------------------------------------- 1 | //! ADC interface circular DMA RX transfer test 2 | 3 | #![allow(clippy::empty_loop)] 4 | #![no_main] 5 | #![no_std] 6 | 7 | use panic_halt as _; 8 | 9 | use cortex_m::{asm, singleton}; 10 | 11 | use cortex_m_rt::entry; 12 | use stm32f1xx_hal::{adc, dma::Half, pac, prelude::*}; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | // Acquire peripherals 17 | let p = pac::Peripherals::take().unwrap(); 18 | let mut flash = p.FLASH.constrain(); 19 | let rcc = p.RCC.constrain(); 20 | 21 | // Configure ADC clocks 22 | // Default value is the slowest possible ADC clock: PCLK2 / 8. Meanwhile ADC 23 | // clock is configurable. So its frequency may be tweaked to meet certain 24 | // practical needs. User specified value is be approximated using supported 25 | // prescaler values 2/4/6/8. 26 | let clocks = rcc.cfgr.adcclk(2.MHz()).freeze(&mut flash.acr); 27 | 28 | let dma_ch1 = p.DMA1.split().1; 29 | 30 | // Setup ADC 31 | let adc1 = adc::Adc::new(p.ADC1, &clocks); 32 | 33 | // Setup GPIOA 34 | let mut gpioa = p.GPIOA.split(); 35 | 36 | // Configure pa0 as an analog input 37 | let adc_ch0 = gpioa.pa0.into_analog(&mut gpioa.crl); 38 | 39 | let adc_dma = adc1.with_dma(adc_ch0, dma_ch1); 40 | let buf = singleton!(: [[u16; 8]; 2] = [[0; 8]; 2]).unwrap(); 41 | 42 | let mut circ_buffer = adc_dma.circ_read(buf); 43 | 44 | while circ_buffer.readable_half().unwrap() != Half::First {} 45 | 46 | let _first_half = circ_buffer.peek(|half, _| *half).unwrap(); 47 | 48 | while circ_buffer.readable_half().unwrap() != Half::Second {} 49 | 50 | let _second_half = circ_buffer.peek(|half, _| *half).unwrap(); 51 | 52 | let (_buf, adc_dma) = circ_buffer.stop(); 53 | let (_adc1, _adc_ch0, _dma_ch1) = adc_dma.split(); 54 | asm::bkpt(); 55 | 56 | loop {} 57 | } 58 | -------------------------------------------------------------------------------- /examples/adc-dma-rx.rs: -------------------------------------------------------------------------------- 1 | //! ADC interface DMA RX transfer test 2 | 3 | #![allow(clippy::empty_loop)] 4 | #![no_main] 5 | #![no_std] 6 | 7 | use panic_halt as _; 8 | 9 | use cortex_m::{asm, singleton}; 10 | 11 | use cortex_m_rt::entry; 12 | use stm32f1xx_hal::{adc, pac, prelude::*}; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | // Acquire peripherals 17 | let p = pac::Peripherals::take().unwrap(); 18 | let mut flash = p.FLASH.constrain(); 19 | let rcc = p.RCC.constrain(); 20 | 21 | // Configure ADC clocks 22 | // Default value is the slowest possible ADC clock: PCLK2 / 8. Meanwhile ADC 23 | // clock is configurable. So its frequency may be tweaked to meet certain 24 | // practical needs. User specified value is be approximated using supported 25 | // prescaler values 2/4/6/8. 26 | let clocks = rcc.cfgr.adcclk(2.MHz()).freeze(&mut flash.acr); 27 | 28 | let dma_ch1 = p.DMA1.split().1; 29 | 30 | // Setup ADC 31 | let adc1 = adc::Adc::new(p.ADC1, &clocks); 32 | 33 | // Setup GPIOA 34 | let mut gpioa = p.GPIOA.split(); 35 | 36 | // Configure pa0 as an analog input 37 | let adc_ch0 = gpioa.pa0.into_analog(&mut gpioa.crl); 38 | 39 | let adc_dma = adc1.with_dma(adc_ch0, dma_ch1); 40 | let buf = singleton!(: [u16; 8] = [0; 8]).unwrap(); 41 | 42 | // The read method consumes the buf and self, starts the adc and dma transfer and returns a 43 | // RxDma struct. The wait method consumes the RxDma struct, waits for the whole transfer to be 44 | // completed and then returns the updated buf and underlying adc_dma struct. For non blocking, 45 | // one can call the is_done method of RxDma and only call wait after that method returns true. 46 | let (_buf, adc_dma) = adc_dma.read(buf).wait(); 47 | asm::bkpt(); 48 | 49 | // Consumes the AdcDma struct, restores adc configuration to previous state and returns the 50 | // Adc struct in normal mode. 51 | let (_adc1, _adc_ch0, _dma_ch1) = adc_dma.split(); 52 | asm::bkpt(); 53 | 54 | loop {} 55 | } 56 | -------------------------------------------------------------------------------- /examples/adc.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![no_main] 3 | #![no_std] 4 | 5 | use panic_semihosting as _; 6 | 7 | use cortex_m_rt::entry; 8 | use stm32f1xx_hal::{adc, pac, prelude::*}; 9 | 10 | use cortex_m_semihosting::hprintln; 11 | 12 | #[entry] 13 | fn main() -> ! { 14 | // Acquire peripherals 15 | let p = pac::Peripherals::take().unwrap(); 16 | let mut flash = p.FLASH.constrain(); 17 | let rcc = p.RCC.constrain(); 18 | 19 | // Configure ADC clocks 20 | // Default value is the slowest possible ADC clock: PCLK2 / 8. Meanwhile ADC 21 | // clock is configurable. So its frequency may be tweaked to meet certain 22 | // practical needs. User specified value is be approximated using supported 23 | // prescaler values 2/4/6/8. 24 | let clocks = rcc.cfgr.adcclk(2.MHz()).freeze(&mut flash.acr); 25 | hprintln!("adc freq: {}", clocks.adcclk()); 26 | 27 | // Setup ADC 28 | let mut adc1 = adc::Adc::new(p.ADC1, &clocks); 29 | 30 | #[cfg(any(feature = "stm32f103", feature = "connectivity"))] 31 | let mut adc2 = adc::Adc::new(p.ADC2, &clocks); 32 | 33 | // Setup GPIOB 34 | let mut gpiob = p.GPIOB.split(); 35 | 36 | // Configure pb0, pb1 as an analog input 37 | let mut ch0 = gpiob.pb0.into_analog(&mut gpiob.crl); 38 | 39 | #[cfg(any(feature = "stm32f103", feature = "connectivity"))] 40 | let mut ch1 = gpiob.pb1.into_analog(&mut gpiob.crl); 41 | 42 | loop { 43 | let data: u16 = adc1.read(&mut ch0).unwrap(); 44 | hprintln!("adc1: {}", data); 45 | 46 | #[cfg(any(feature = "stm32f103", feature = "connectivity"))] 47 | { 48 | let data1: u16 = adc2.read(&mut ch1).unwrap(); 49 | hprintln!("adc2: {}", data1); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/adc_temperature.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![no_main] 3 | #![no_std] 4 | 5 | use panic_semihosting as _; 6 | 7 | use cortex_m_rt::entry; 8 | use stm32f1xx_hal::{pac, prelude::*}; 9 | 10 | use cortex_m_semihosting::hprintln; 11 | 12 | #[entry] 13 | fn main() -> ! { 14 | // Acquire peripherals 15 | let p = pac::Peripherals::take().unwrap(); 16 | let mut flash = p.FLASH.constrain(); 17 | let rcc = p.RCC.constrain(); 18 | 19 | let clocks = rcc 20 | .cfgr 21 | .use_hse(8.MHz()) 22 | .sysclk(56.MHz()) 23 | .pclk1(28.MHz()) 24 | .adcclk(14.MHz()) 25 | .freeze(&mut flash.acr); 26 | /* 27 | // Alternative configuration using dividers and multipliers directly 28 | let clocks = rcc.cfgr.freeze_with_config(rcc::Config { 29 | hse: Some(8_000_000), 30 | pllmul: Some(7), 31 | hpre: rcc::HPre::DIV1, 32 | ppre1: rcc::PPre::DIV2, 33 | ppre2: rcc::PPre::DIV1, 34 | usbpre: rcc::UsbPre::DIV1_5, 35 | adcpre: rcc::AdcPre::DIV2, 36 | }, &mut flash.acr);*/ 37 | hprintln!("sysclk freq: {}", clocks.sysclk()); 38 | hprintln!("adc freq: {}", clocks.adcclk()); 39 | 40 | // Setup ADC 41 | let mut adc = p.ADC1.adc(&clocks); 42 | 43 | // Read temperature sensor 44 | loop { 45 | let temp = adc.read_temp(); 46 | 47 | hprintln!("temp: {}", temp); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/blinky.rs: -------------------------------------------------------------------------------- 1 | //! Blinks an LED 2 | //! 3 | //! This assumes that a LED is connected to pc13 as is the case on the blue pill board. 4 | //! 5 | //! Note: Without additional hardware, PC13 should not be used to drive an LED, see page 5.1.2 of 6 | //! the reference manual for an explanation. This is not an issue on the blue pill. 7 | 8 | #![deny(unsafe_code)] 9 | #![no_std] 10 | #![no_main] 11 | 12 | use panic_halt as _; 13 | 14 | use nb::block; 15 | 16 | use cortex_m_rt::entry; 17 | use stm32f1xx_hal::{pac, prelude::*, timer::Timer}; 18 | 19 | #[entry] 20 | fn main() -> ! { 21 | // Get access to the core peripherals from the cortex-m crate 22 | let cp = cortex_m::Peripherals::take().unwrap(); 23 | // Get access to the device specific peripherals from the peripheral access crate 24 | let dp = pac::Peripherals::take().unwrap(); 25 | 26 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 27 | // HAL structs 28 | let mut flash = dp.FLASH.constrain(); 29 | let rcc = dp.RCC.constrain(); 30 | 31 | // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 32 | // `clocks` 33 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 34 | 35 | // Acquire the GPIOC peripheral 36 | let mut gpioc = dp.GPIOC.split(); 37 | 38 | // Configure gpio C pin 13 as a push-pull output. The `crh` register is passed to the function 39 | // in order to configure the port. For pins 0-7, crl should be passed instead. 40 | let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 41 | // Configure the syst timer to trigger an update every second 42 | let mut timer = Timer::syst(cp.SYST, &clocks).counter_hz(); 43 | timer.start(1.Hz()).unwrap(); 44 | 45 | // Wait for the timer to trigger an update and change the state of the LED 46 | loop { 47 | block!(timer.wait()).unwrap(); 48 | led.set_high(); 49 | block!(timer.wait()).unwrap(); 50 | led.set_low(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/blinky_generic.rs: -------------------------------------------------------------------------------- 1 | //! Blinks several LEDs stored in an array 2 | 3 | #![deny(unsafe_code)] 4 | #![no_std] 5 | #![no_main] 6 | 7 | use panic_halt as _; 8 | 9 | use nb::block; 10 | 11 | use cortex_m_rt::entry; 12 | use stm32f1xx_hal::{pac, prelude::*, timer::Timer}; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | let cp = cortex_m::Peripherals::take().unwrap(); 17 | let dp = pac::Peripherals::take().unwrap(); 18 | 19 | let mut flash = dp.FLASH.constrain(); 20 | let rcc = dp.RCC.constrain(); 21 | 22 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 23 | 24 | // Acquire the GPIO peripherals 25 | let mut gpioa = dp.GPIOA.split(); 26 | let mut gpioc = dp.GPIOC.split(); 27 | 28 | // Configure the syst timer to trigger an update every second 29 | let mut timer = Timer::syst(cp.SYST, &clocks).counter_hz(); 30 | timer.start(1.Hz()).unwrap(); 31 | 32 | // Create an array of LEDS to blink 33 | let mut leds = [ 34 | gpioc.pc13.into_push_pull_output(&mut gpioc.crh).erase(), 35 | gpioa.pa1.into_push_pull_output(&mut gpioa.crl).erase(), 36 | ]; 37 | 38 | // Wait for the timer to trigger an update and change the state of the LED 39 | loop { 40 | block!(timer.wait()).unwrap(); 41 | for led in leds.iter_mut() { 42 | led.set_high(); 43 | } 44 | block!(timer.wait()).unwrap(); 45 | for led in leds.iter_mut() { 46 | led.set_low(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/blinky_rtc.rs: -------------------------------------------------------------------------------- 1 | //! Blinks an LED 2 | //! 3 | //! This assumes that a LED is connected to pc13 as is the case on the blue pill board. 4 | //! 5 | //! Note: Without additional hardware, PC13 should not be used to drive a LED, see 6 | //! section 5.1.2 of the reference manual for an explanation. 7 | //! This is not an issue on the blue pill. 8 | 9 | #![deny(unsafe_code)] 10 | #![no_std] 11 | #![no_main] 12 | 13 | use panic_halt as _; 14 | 15 | use stm32f1xx_hal::{pac, prelude::*, rtc::Rtc}; 16 | 17 | use cortex_m_rt::entry; 18 | use nb::block; 19 | 20 | #[entry] 21 | fn main() -> ! { 22 | let dp = pac::Peripherals::take().unwrap(); 23 | 24 | let mut pwr = dp.PWR; 25 | let rcc = dp.RCC.constrain(); 26 | 27 | // Set up the GPIO pin 28 | let mut gpioc = dp.GPIOC.split(); 29 | let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 30 | 31 | // Set up the RTC 32 | // Enable writes to the backup domain 33 | let mut backup_domain = rcc.bkp.constrain(dp.BKP, &mut pwr); 34 | // Start the RTC 35 | let mut rtc = Rtc::new(dp.RTC, &mut backup_domain); 36 | 37 | let mut led_on = false; 38 | loop { 39 | // Set the current time to 0 40 | rtc.set_time(0); 41 | // Trigger the alarm in 5 seconds 42 | rtc.set_alarm(5); 43 | block!(rtc.wait_alarm()).unwrap(); 44 | if led_on { 45 | led.set_low(); 46 | led_on = false; 47 | } else { 48 | led.set_high(); 49 | led_on = true; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/blinky_rtcalarm_irq.rs: -------------------------------------------------------------------------------- 1 | //! Blinks an LED 2 | //! 3 | //! This assumes that a LED is connected to pc13 as is the case on the blue pill board. 4 | //! 5 | //! Please note according to RM0008: 6 | //! "Due to the fact that the switch only sinks a limited amount of current (3 mA), the use of 7 | //! GPIOs PC13 to PC15 in output mode is restricted: the speed has to be limited to 2MHz with 8 | //! a maximum load of 30pF and these IOs must not be used as a current source (e.g. to drive a LED)" 9 | 10 | #![allow(clippy::empty_loop)] 11 | #![allow(unused)] 12 | #![no_std] 13 | #![no_main] 14 | 15 | use panic_halt as _; 16 | 17 | use stm32f1xx_hal as hal; 18 | 19 | use crate::hal::{ 20 | gpio::{gpioc, Output, PushPull}, 21 | pac::{interrupt, Interrupt, Peripherals, EXTI}, 22 | prelude::*, 23 | rtc::Rtc, 24 | }; 25 | 26 | use core::cell::RefCell; 27 | use cortex_m::{asm::wfi, interrupt::Mutex}; 28 | use cortex_m_rt::entry; 29 | 30 | // A type definition for the GPIO pin to be used for our LED 31 | type LedPin = gpioc::PC13>; 32 | 33 | // Make LED pin globally available 34 | static G_LED: Mutex>> = Mutex::new(RefCell::new(None)); 35 | // Make RTC globally available 36 | static G_RTC: Mutex>> = Mutex::new(RefCell::new(None)); 37 | // Make EXTI registers globally available 38 | static G_EXTI: Mutex>> = Mutex::new(RefCell::new(None)); 39 | 40 | // Toggle LED every 3 seconds 41 | const TOGGLE_INTERVAL_SECONDS: u32 = 3; 42 | 43 | // The f100 does not have an RTC, so this example is disabled 44 | #[cfg(feature = "stm32f101")] 45 | #[entry] 46 | fn main() -> ! { 47 | loop { 48 | continue; 49 | } 50 | } 51 | 52 | #[cfg(not(feature = "stm32f101"))] 53 | #[interrupt] 54 | fn RTCALARM() { 55 | static mut LED: Option = None; 56 | static mut RTC: Option = None; 57 | static mut EXTI: Option = None; 58 | 59 | let led = LED.get_or_insert_with(|| { 60 | cortex_m::interrupt::free(|cs| G_LED.borrow(cs).replace(None).unwrap()) 61 | }); 62 | let rtc = RTC.get_or_insert_with(|| { 63 | cortex_m::interrupt::free(|cs| G_RTC.borrow(cs).replace(None).unwrap()) 64 | }); 65 | let exti = EXTI.get_or_insert_with(|| { 66 | cortex_m::interrupt::free(|cs| G_EXTI.borrow(cs).replace(None).unwrap()) 67 | }); 68 | 69 | exti.pr().write(|w| w.pr17().clear_bit_by_one()); 70 | rtc.set_alarm(rtc.current_time() + TOGGLE_INTERVAL_SECONDS); 71 | 72 | let _ = led.toggle(); 73 | } 74 | 75 | #[cfg(not(feature = "stm32f101"))] 76 | #[entry] 77 | fn main() -> ! { 78 | let dp = Peripherals::take().unwrap(); 79 | 80 | let mut pwr = dp.PWR; 81 | let rcc = dp.RCC.constrain(); 82 | 83 | // Set up the GPIO pin 84 | let mut gpioc = dp.GPIOC.split(); 85 | let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 86 | let _ = led.set_high(); // Turn off 87 | 88 | cortex_m::interrupt::free(|cs| *G_LED.borrow(cs).borrow_mut() = Some(led)); 89 | 90 | // Set up the EXTI (see notes in section 18.4.2 of reference manual) 91 | let exti = dp.EXTI; 92 | exti.ftsr().write(|w| w.tr17().set_bit()); 93 | exti.imr().write(|w| w.mr17().set_bit()); 94 | 95 | cortex_m::interrupt::free(|cs| *G_EXTI.borrow(cs).borrow_mut() = Some(exti)); 96 | 97 | // Set up the RTC 98 | // Enable writes to the backup domain 99 | let mut backup_domain = rcc.bkp.constrain(dp.BKP, &mut pwr); 100 | // Start the RTC 101 | let mut rtc = Rtc::new(dp.RTC, &mut backup_domain); 102 | rtc.set_time(0); 103 | rtc.set_alarm(TOGGLE_INTERVAL_SECONDS); 104 | rtc.listen_alarm(); 105 | 106 | cortex_m::interrupt::free(|cs| *G_RTC.borrow(cs).borrow_mut() = Some(rtc)); 107 | 108 | // Enable RTCALARM IRQ 109 | unsafe { cortex_m::peripheral::NVIC::unmask(Interrupt::RTCALARM) }; 110 | 111 | loop { 112 | wfi(); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /examples/blinky_timer_irq.rs: -------------------------------------------------------------------------------- 1 | //! blinky timer using interrupts on TIM2 2 | //! 3 | //! This assumes that a LED is connected to pc13 as is the case on the blue pill board. 4 | //! 5 | //! Please note according to RM0008: 6 | //! "Due to the fact that the switch only sinks a limited amount of current (3 mA), the use of 7 | //! GPIOs PC13 to PC15 in output mode is restricted: the speed has to be limited to 2MHz with 8 | //! a maximum load of 30pF and these IOs must not be used as a current source (e.g. to drive a LED)" 9 | 10 | #![no_main] 11 | #![no_std] 12 | 13 | use panic_halt as _; 14 | 15 | use stm32f1xx_hal as hal; 16 | 17 | use crate::hal::{ 18 | gpio::{gpioc, Output, PinState, PushPull}, 19 | pac::{interrupt, Interrupt, Peripherals, TIM2}, 20 | prelude::*, 21 | timer::{CounterMs, Event}, 22 | }; 23 | 24 | use core::cell::RefCell; 25 | use cortex_m::{asm::wfi, interrupt::Mutex}; 26 | use cortex_m_rt::entry; 27 | 28 | // NOTE You can uncomment 'hprintln' here and in the code below for a bit more 29 | // verbosity at runtime, at the cost of throwing off the timing of the blink 30 | // (using 'semihosting' for printing debug info anywhere slows program 31 | // execution down) 32 | //use cortex_m_semihosting::hprintln; 33 | 34 | // A type definition for the GPIO pin to be used for our LED 35 | type LedPin = gpioc::PC13>; 36 | 37 | // Make LED pin globally available 38 | static G_LED: Mutex>> = Mutex::new(RefCell::new(None)); 39 | 40 | // Make timer interrupt registers globally available 41 | static G_TIM: Mutex>>> = Mutex::new(RefCell::new(None)); 42 | 43 | // Define an interupt handler, i.e. function to call when interrupt occurs. 44 | // This specific interrupt will "trip" when the timer TIM2 times out 45 | #[interrupt] 46 | fn TIM2() { 47 | static mut LED: Option = None; 48 | static mut TIM: Option> = None; 49 | 50 | let led = LED.get_or_insert_with(|| { 51 | cortex_m::interrupt::free(|cs| { 52 | // Move LED pin here, leaving a None in its place 53 | G_LED.borrow(cs).replace(None).unwrap() 54 | }) 55 | }); 56 | 57 | let tim = TIM.get_or_insert_with(|| { 58 | cortex_m::interrupt::free(|cs| { 59 | // Move LED pin here, leaving a None in its place 60 | G_TIM.borrow(cs).replace(None).unwrap() 61 | }) 62 | }); 63 | 64 | let _ = led.toggle(); 65 | let _ = tim.wait(); 66 | } 67 | 68 | #[entry] 69 | fn main() -> ! { 70 | let dp = Peripherals::take().unwrap(); 71 | 72 | let rcc = dp.RCC.constrain(); 73 | let mut flash = dp.FLASH.constrain(); 74 | let clocks = rcc 75 | .cfgr 76 | .sysclk(8.MHz()) 77 | .pclk1(8.MHz()) 78 | .freeze(&mut flash.acr); 79 | 80 | // Configure PC13 pin to blink LED 81 | let mut gpioc = dp.GPIOC.split(); 82 | let led = Output::new(gpioc.pc13, &mut gpioc.crh, PinState::High); 83 | //or 84 | //let led = gpioc.pc13.into_push_pull_output_with_state(&mut gpioc.crh, PinState::High); 85 | 86 | // Move the pin into our global storage 87 | cortex_m::interrupt::free(|cs| *G_LED.borrow(cs).borrow_mut() = Some(led)); 88 | 89 | // Set up a timer expiring after 1s 90 | let mut timer = dp.TIM2.counter_ms(&clocks); 91 | timer.start(1.secs()).unwrap(); 92 | 93 | // Generate an interrupt when the timer expires 94 | timer.listen(Event::Update); 95 | 96 | // Move the timer into our global storage 97 | cortex_m::interrupt::free(|cs| *G_TIM.borrow(cs).borrow_mut() = Some(timer)); 98 | 99 | unsafe { 100 | cortex_m::peripheral::NVIC::unmask(Interrupt::TIM2); 101 | } 102 | 103 | loop { 104 | wfi(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /examples/can-echo.rs: -------------------------------------------------------------------------------- 1 | //! Simple CAN example. 2 | //! Requires a transceiver connected to PA11, PA12 (CAN1) or PB5 PB6 (CAN2). 3 | 4 | #![no_main] 5 | #![no_std] 6 | 7 | use bxcan::Fifo; 8 | use panic_halt as _; 9 | 10 | use bxcan::filter::Mask32; 11 | use cortex_m_rt::entry; 12 | use nb::block; 13 | use stm32f1xx_hal::{pac, prelude::*}; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | let dp = pac::Peripherals::take().unwrap(); 18 | 19 | let mut flash = dp.FLASH.constrain(); 20 | let rcc = dp.RCC.constrain(); 21 | 22 | // To meet CAN clock accuracy requirements an external crystal or ceramic 23 | // resonator must be used. The blue pill has a 8MHz external crystal. 24 | // Other boards might have a crystal with another frequency or none at all. 25 | rcc.cfgr.use_hse(8.MHz()).freeze(&mut flash.acr); 26 | 27 | let mut can1 = { 28 | let gpioa = dp.GPIOA.split(); 29 | let rx = gpioa.pa11; 30 | let tx = gpioa.pa12; 31 | 32 | #[cfg(not(feature = "connectivity"))] 33 | let can = dp.CAN.can(dp.USB, (tx, rx)); 34 | #[cfg(feature = "connectivity")] 35 | let can = dp.CAN1.can((tx, rx)); 36 | 37 | // APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5% 38 | // Value was calculated with http://www.bittiming.can-wiki.info/ 39 | bxcan::Can::builder(can) 40 | .set_bit_timing(0x001c_0003) 41 | .leave_disabled() 42 | }; 43 | 44 | // Configure filters so that can frames can be received. 45 | let mut filters = can1.modify_filters(); 46 | filters.enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); 47 | 48 | #[cfg(feature = "connectivity")] 49 | let _can2 = { 50 | let gpiob = dp.GPIOB.split(); 51 | let can = dp.CAN2.can((gpiob.pb6, gpiob.pb5)); 52 | 53 | // APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5% 54 | // Value was calculated with http://www.bittiming.can-wiki.info/ 55 | let can2 = bxcan::Can::builder(can) 56 | .set_bit_timing(0x001c_0003) 57 | .leave_disabled(); 58 | 59 | // A total of 28 filters are shared between the two CAN instances. 60 | // Split them equally between CAN1 and CAN2. 61 | let mut slave_filters = filters.set_split(14).slave_filters(); 62 | slave_filters.enable_bank(14, Fifo::Fifo0, Mask32::accept_all()); 63 | can2 64 | }; 65 | 66 | // Drop filters to leave filter configuraiton mode. 67 | drop(filters); 68 | 69 | // Select the interface. 70 | let mut can = can1; 71 | //let mut can = _can2; 72 | 73 | // Split the peripheral into transmitter and receiver parts. 74 | block!(can.enable_non_blocking()).unwrap(); 75 | 76 | // Echo back received packages in sequence. 77 | // See the `can-rtfm` example for an echo implementation that adheres to 78 | // correct frame ordering based on the transfer id. 79 | loop { 80 | if let Ok(frame) = block!(can.receive()) { 81 | block!(can.transmit(&frame)).unwrap(); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /examples/can-loopback.rs: -------------------------------------------------------------------------------- 1 | //! Showcases advanced CAN filter capabilities. 2 | //! Does not require additional transceiver hardware. 3 | 4 | #![no_main] 5 | #![no_std] 6 | 7 | use bxcan::{ 8 | filter::{ListEntry16, ListEntry32, Mask16}, 9 | ExtendedId, Fifo, Frame, StandardId, 10 | }; 11 | use panic_halt as _; 12 | 13 | use cortex_m_rt::entry; 14 | use nb::block; 15 | use stm32f1xx_hal::{can::Can, gpio::Floating, pac, prelude::*}; 16 | 17 | #[entry] 18 | fn main() -> ! { 19 | let dp = pac::Peripherals::take().unwrap(); 20 | 21 | let mut flash = dp.FLASH.constrain(); 22 | let rcc = dp.RCC.constrain(); 23 | 24 | // To meet CAN clock accuracy requirements, an external crystal or ceramic 25 | // resonator must be used. 26 | rcc.cfgr.use_hse(8.MHz()).freeze(&mut flash.acr); 27 | 28 | #[cfg(not(feature = "connectivity"))] 29 | let can = Can::<_, Floating>::new_loopback(dp.CAN, dp.USB); 30 | #[cfg(feature = "connectivity")] 31 | let can = Can::<_, Floating>::new_loopback(dp.CAN1); 32 | 33 | // Use loopback mode: No pins need to be assigned to peripheral. 34 | // APB1 (PCLK1): 8MHz, Bit rate: 500Bit/s, Sample Point 87.5% 35 | // Value was calculated with http://www.bittiming.can-wiki.info/ 36 | let mut can = bxcan::Can::builder(can) 37 | .set_bit_timing(0x001c_0000) 38 | .set_loopback(true) 39 | .set_silent(true) 40 | .leave_disabled(); 41 | 42 | let mut filters = can.modify_filters(); 43 | assert!(filters.num_banks() > 3); 44 | 45 | // The order of the added filters is important: it must match configuration 46 | // of the `split_filters_advanced()` method. 47 | 48 | // 2x 11bit id + mask filter bank: Matches 0, 1, 2 49 | // TODO: Make this accept also ID 2 50 | filters.enable_bank( 51 | 0, 52 | Fifo::Fifo0, 53 | [ 54 | // accepts 0 and 1 55 | Mask16::frames_with_std_id(StandardId::new(0).unwrap(), StandardId::new(1).unwrap()), 56 | // accepts 0 and 2 57 | Mask16::frames_with_std_id(StandardId::new(0).unwrap(), StandardId::new(2).unwrap()), 58 | ], 59 | ); 60 | 61 | // 2x 29bit id filter bank: Matches 4, 5 62 | filters.enable_bank( 63 | 1, 64 | Fifo::Fifo0, 65 | [ 66 | ListEntry32::data_frames_with_id(ExtendedId::new(4).unwrap()), 67 | ListEntry32::data_frames_with_id(ExtendedId::new(5).unwrap()), 68 | ], 69 | ); 70 | 71 | // 4x 11bit id filter bank: Matches 8, 9, 10, 11 72 | filters.enable_bank( 73 | 2, 74 | Fifo::Fifo0, 75 | [ 76 | ListEntry16::data_frames_with_id(StandardId::new(8).unwrap()), 77 | ListEntry16::data_frames_with_id(StandardId::new(9).unwrap()), 78 | ListEntry16::data_frames_with_id(StandardId::new(10).unwrap()), 79 | ListEntry16::data_frames_with_id(StandardId::new(11).unwrap()), 80 | ], 81 | ); 82 | 83 | // Enable filters. 84 | drop(filters); 85 | 86 | // Sync to the bus and start normal operation. 87 | block!(can.enable_non_blocking()).ok(); 88 | 89 | // Some messages shall pass the filters. 90 | for &id in &[0, 1, 2, 8, 9, 10, 11] { 91 | let frame_tx = Frame::new_data(StandardId::new(id).unwrap(), [id as u8]); 92 | block!(can.transmit(&frame_tx)).unwrap(); 93 | let frame_rx = block!(can.receive()).unwrap(); 94 | assert_eq!(frame_tx, frame_rx); 95 | } 96 | for &id in &[4, 5] { 97 | let frame_tx = Frame::new_data(ExtendedId::new(id).unwrap(), [id as u8]); 98 | block!(can.transmit(&frame_tx)).unwrap(); 99 | let frame_rx = block!(can.receive()).unwrap(); 100 | assert_eq!(frame_tx, frame_rx); 101 | } 102 | 103 | // Some messages shall not be received. 104 | for &id in &[3, 6, 7, 12] { 105 | let frame_tx = Frame::new_data(ExtendedId::new(id).unwrap(), [id as u8]); 106 | block!(can.transmit(&frame_tx)).unwrap(); 107 | while !can.is_transmitter_idle() {} 108 | 109 | assert!(can.receive().is_err()); 110 | } 111 | 112 | let mut gpiob = dp.GPIOB.split(); 113 | let mut led = gpiob.pb9.into_push_pull_output(&mut gpiob.crh); 114 | led.set_high(); 115 | 116 | loop {} 117 | } 118 | -------------------------------------------------------------------------------- /examples/crc.rs: -------------------------------------------------------------------------------- 1 | //! CRC calculation 2 | 3 | #![deny(unsafe_code)] 4 | #![allow(clippy::empty_loop)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | use panic_halt as _; 9 | 10 | use cortex_m_rt::entry; 11 | use cortex_m_semihosting::hprintln; 12 | use stm32f1xx_hal::{pac, prelude::*}; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | let p = pac::Peripherals::take().unwrap(); 17 | 18 | let mut crc = p.CRC.new(); 19 | 20 | crc.reset(); 21 | crc.write(0x12345678); 22 | 23 | let val = crc.read(); 24 | hprintln!("found={:08x}, expected={:08x}", val, 0xdf8a8a2b_u32); 25 | 26 | loop {} 27 | } 28 | -------------------------------------------------------------------------------- /examples/delay-timer-blinky.rs: -------------------------------------------------------------------------------- 1 | //! Demonstrate the use of a blocking `Delay` using TIM2 general-purpose timer. 2 | 3 | #![deny(unsafe_code)] 4 | #![allow(clippy::empty_loop)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | // Halt on panic 9 | use panic_halt as _; // panic handler 10 | 11 | use cortex_m_rt::entry; 12 | use stm32f1xx_hal as hal; 13 | 14 | use crate::hal::{pac, prelude::*}; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | if let (Some(dp), Some(_cp)) = ( 19 | pac::Peripherals::take(), 20 | cortex_m::peripheral::Peripherals::take(), 21 | ) { 22 | let mut flash = dp.FLASH.constrain(); 23 | // Set up the LED. On the BluePill it's connected to pin PC13. 24 | let mut gpioc = dp.GPIOC.split(); 25 | let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 26 | 27 | // Set up the system clock. We want to run at 48MHz for this one. 28 | let rcc = dp.RCC.constrain(); 29 | let clocks = rcc 30 | .cfgr 31 | .use_hse(8.MHz()) 32 | .sysclk(48.MHz()) 33 | .freeze(&mut flash.acr); 34 | 35 | // Create a delay abstraction based on general-pupose 32-bit timer TIM2 36 | 37 | //let mut delay = hal::timer::FTimerUs::new(dp.TIM2, &clocks).delay(); 38 | // or 39 | let mut delay = dp.TIM2.delay_us(&clocks); 40 | 41 | loop { 42 | // On for 1s, off for 3s. 43 | led.set_high(); 44 | // Use `embedded_hal_02::DelayMs` trait 45 | delay.delay_ms(1000_u32); 46 | led.set_low(); 47 | // or use `fugit` duration units 48 | delay.delay(3.secs()); 49 | } 50 | } 51 | 52 | loop {} 53 | } 54 | -------------------------------------------------------------------------------- /examples/delay.rs: -------------------------------------------------------------------------------- 1 | //! "Blinky" using delays instead of a timer 2 | 3 | #![deny(unsafe_code)] 4 | #![no_main] 5 | #![no_std] 6 | 7 | use panic_halt as _; 8 | 9 | use cortex_m_rt::entry; 10 | use stm32f1xx_hal::{pac, prelude::*}; 11 | 12 | #[entry] 13 | fn main() -> ! { 14 | let dp = pac::Peripherals::take().unwrap(); 15 | let cp = cortex_m::Peripherals::take().unwrap(); 16 | 17 | let mut flash = dp.FLASH.constrain(); 18 | let rcc = dp.RCC.constrain(); 19 | 20 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 21 | 22 | let mut gpioc = dp.GPIOC.split(); 23 | 24 | #[cfg(feature = "stm32f100")] 25 | let mut led = gpioc.pc9.into_push_pull_output(&mut gpioc.crh); 26 | 27 | #[cfg(feature = "stm32f101")] 28 | let mut led = gpioc.pc9.into_push_pull_output(&mut gpioc.crh); 29 | 30 | #[cfg(any(feature = "stm32f103", feature = "stm32f105", feature = "stm32f107"))] 31 | let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 32 | 33 | //let mut delay = hal::timer::Timer::syst(cp.SYST, &clocks).delay(); 34 | // or 35 | let mut delay = cp.SYST.delay(&clocks); 36 | 37 | loop { 38 | led.set_high(); 39 | // Use `embedded_hal_02::DelayMs` trait 40 | delay.delay_ms(1_000_u16); 41 | led.set_low(); 42 | // or use `fugit` duration units 43 | delay.delay(1.secs()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/dynamic_gpio.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![no_std] 3 | #![no_main] 4 | 5 | use panic_halt as _; 6 | 7 | use nb::block; 8 | 9 | use cortex_m_rt::entry; 10 | use cortex_m_semihosting::hprintln; 11 | use embedded_hal_02::digital::v2::{InputPin, OutputPin}; 12 | use stm32f1xx_hal::{pac, prelude::*}; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | // Get access to the core peripherals from the cortex-m crate 17 | let cp = cortex_m::Peripherals::take().unwrap(); 18 | // Get access to the device specific peripherals from the peripheral access crate 19 | let dp = pac::Peripherals::take().unwrap(); 20 | 21 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 22 | // HAL structs 23 | let mut flash = dp.FLASH.constrain(); 24 | let rcc = dp.RCC.constrain(); 25 | 26 | // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 27 | // `clocks` 28 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 29 | 30 | // Acquire the GPIOC peripheral 31 | let mut gpioc = dp.GPIOC.split(); 32 | 33 | let mut pin = gpioc.pc13.into_dynamic(&mut gpioc.crh); 34 | // Configure the syst timer to trigger an update every second 35 | let mut timer = cp.SYST.counter_hz(&clocks); 36 | timer.start(1.Hz()).unwrap(); 37 | 38 | // Wait for the timer to trigger an update and change the state of the LED 39 | loop { 40 | pin.make_floating_input(&mut gpioc.crh); 41 | block!(timer.wait()).unwrap(); 42 | hprintln!("{}", pin.is_high().unwrap()); 43 | 44 | pin.make_push_pull_output(&mut gpioc.crh); 45 | pin.set_high().unwrap(); 46 | block!(timer.wait()).unwrap(); 47 | pin.set_low().unwrap(); 48 | block!(timer.wait()).unwrap(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/exti.rs: -------------------------------------------------------------------------------- 1 | //! Turns the user LED on 2 | //! 3 | //! Listens for interrupts on the pa7 pin. On any rising or falling edge, toggles 4 | //! the pc13 pin (which is connected to the LED on the blue pill board, hence the `led` name). 5 | 6 | #![allow(clippy::empty_loop)] 7 | #![no_main] 8 | #![no_std] 9 | 10 | use panic_halt as _; 11 | 12 | use core::mem::MaybeUninit; 13 | use cortex_m_rt::entry; 14 | use pac::interrupt; 15 | use stm32f1xx_hal::gpio::*; 16 | use stm32f1xx_hal::{pac, prelude::*}; 17 | 18 | // These two are owned by the ISR. main() may only access them during the initialization phase, 19 | // where the interrupt is not yet enabled (i.e. no concurrent accesses can occur). 20 | // After enabling the interrupt, main() may not have any references to these objects any more. 21 | // For the sake of minimalism, we do not use RTIC here, which would be the better way. 22 | static mut LED: MaybeUninit>> = 23 | MaybeUninit::uninit(); 24 | static mut INT_PIN: MaybeUninit>> = 25 | MaybeUninit::uninit(); 26 | 27 | #[interrupt] 28 | fn EXTI9_5() { 29 | let led = unsafe { &mut *LED.as_mut_ptr() }; 30 | let int_pin = unsafe { &mut *INT_PIN.as_mut_ptr() }; 31 | 32 | if int_pin.check_interrupt() { 33 | led.toggle(); 34 | 35 | // if we don't clear this bit, the ISR would trigger indefinitely 36 | int_pin.clear_interrupt_pending_bit(); 37 | } 38 | } 39 | 40 | #[entry] 41 | fn main() -> ! { 42 | // initialization phase 43 | let mut p = pac::Peripherals::take().unwrap(); 44 | let _cp = cortex_m::peripheral::Peripherals::take().unwrap(); 45 | { 46 | // the scope ensures that the int_pin reference is dropped before the first ISR can be executed. 47 | 48 | let mut gpioa = p.GPIOA.split(); 49 | let mut gpioc = p.GPIOC.split(); 50 | let mut afio = p.AFIO.constrain(); 51 | 52 | let led = unsafe { &mut *LED.as_mut_ptr() }; 53 | *led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 54 | 55 | let int_pin = unsafe { &mut *INT_PIN.as_mut_ptr() }; 56 | *int_pin = gpioa.pa7.into_floating_input(&mut gpioa.crl); 57 | int_pin.make_interrupt_source(&mut afio); 58 | int_pin.trigger_on_edge(&mut p.EXTI, Edge::RisingFalling); 59 | int_pin.enable_interrupt(&mut p.EXTI); 60 | } // initialization ends here 61 | 62 | unsafe { 63 | pac::NVIC::unmask(pac::Interrupt::EXTI9_5); 64 | } 65 | 66 | loop {} 67 | } 68 | -------------------------------------------------------------------------------- /examples/exti_rtic.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![deny(warnings)] 3 | #![no_std] 4 | #![no_main] 5 | 6 | use panic_halt as _; 7 | 8 | #[rtic::app(device = stm32f1xx_hal::pac)] 9 | mod app { 10 | use stm32f1xx_hal::{ 11 | gpio::{gpioa::PA0, gpioc::PC13, Edge, ExtiPin, Input, Output, PullDown, PushPull}, 12 | prelude::*, 13 | }; 14 | 15 | #[shared] 16 | struct Shared {} 17 | 18 | #[local] 19 | struct Local { 20 | button: PA0>, 21 | led: PC13>, 22 | } 23 | 24 | #[init] 25 | fn init(mut ctx: init::Context) -> (Shared, Local, init::Monotonics) { 26 | let mut afio = ctx.device.AFIO.constrain(); 27 | 28 | let mut gpioc = ctx.device.GPIOC.split(); 29 | let led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 30 | 31 | let mut gpioa = ctx.device.GPIOA.split(); 32 | let mut button = gpioa.pa0.into_pull_down_input(&mut gpioa.crl); 33 | button.make_interrupt_source(&mut afio); 34 | button.enable_interrupt(&mut ctx.device.EXTI); 35 | button.trigger_on_edge(&mut ctx.device.EXTI, Edge::Rising); 36 | 37 | (Shared {}, Local { button, led }, init::Monotonics()) 38 | } 39 | 40 | #[task(binds = EXTI0, local = [button, led])] 41 | fn button_click(ctx: button_click::Context) { 42 | ctx.local.button.clear_interrupt_pending_bit(); 43 | ctx.local.led.toggle(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/gpio_input.rs: -------------------------------------------------------------------------------- 1 | //! Through buttons to control the LED. 2 | //! 3 | //! This assumes that has two LEDs, the red light is connected to pa8 and the green light is connected to pd2. 4 | //! 5 | //! Meanwhile, it has two buttons, we can call them key_0 and key_1. 6 | //! The key_0 is connected to pc5, and the key_1 is connected to pa15. 7 | //! 8 | //! We need to set into_pull_up_input for pc5 and pa15, for the reason that the key_0 and key_1 were connected to GND. 9 | //! 10 | //! Use key_0 to control the red light, key_1 to control the green light. 11 | //! Only press a button after releasing the button to turns on the led, again turns down the led. 12 | //! 13 | //! And the long press was a nullity. 14 | 15 | #![deny(unsafe_code)] 16 | #![no_std] 17 | #![no_main] 18 | use cortex_m_rt::entry; 19 | use panic_halt as _; 20 | use stm32f1xx_hal::{gpio::PinState, pac, prelude::*}; 21 | 22 | #[entry] 23 | fn main() -> ! { 24 | let dp = pac::Peripherals::take().unwrap(); 25 | let cp = cortex_m::Peripherals::take().unwrap(); 26 | 27 | let mut flash = dp.FLASH.constrain(); 28 | let rcc = dp.RCC.constrain(); 29 | 30 | let clock = rcc.cfgr.freeze(&mut flash.acr); 31 | 32 | let mut gpioa = dp.GPIOA.split(); 33 | let mut _gpiob = dp.GPIOB.split(); 34 | let mut gpioc = dp.GPIOC.split(); 35 | let mut gpiod = dp.GPIOD.split(); 36 | 37 | // red_led and green_led 38 | let mut red_led = gpioa 39 | .pa8 40 | .into_push_pull_output_with_state(&mut gpioa.crh, PinState::High); 41 | let mut green_led = gpiod 42 | .pd2 43 | .into_push_pull_output_with_state(&mut gpiod.crl, PinState::High); 44 | 45 | let mut afio = dp.AFIO.constrain(); 46 | let (gpioa_pa15, _gpiob_pb3, _gpiob_pb4) = 47 | afio.mapr.disable_jtag(gpioa.pa15, _gpiob.pb3, _gpiob.pb4); 48 | 49 | // key_0 and key_1 50 | let key_0 = gpioc.pc5.into_pull_up_input(&mut gpioc.crl); 51 | let key_1 = gpioa_pa15.into_pull_up_input(&mut gpioa.crh); 52 | 53 | // The key_up for check buttons if long press. 54 | // if key_up is true, and buttons were not long press. 55 | let mut key_up: bool = true; 56 | let mut delay = cp.SYST.delay(&clock); 57 | loop { 58 | let key_result = (key_0.is_low(), key_1.is_low()); 59 | if key_up && (key_result.0 || key_result.1) { 60 | key_up = false; 61 | delay.delay_ms(10u8); 62 | match key_result { 63 | (true, _) => red_led.toggle(), 64 | (_, true) => green_led.toggle(), 65 | (_, _) => (), 66 | } 67 | } else if !key_result.0 && !key_result.1 { 68 | key_up = true; 69 | delay.delay_ms(10u8); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /examples/hello.rs: -------------------------------------------------------------------------------- 1 | //! Prints "Hello, world" on the OpenOCD console 2 | 3 | #![allow(clippy::empty_loop)] 4 | #![deny(unsafe_code)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | use panic_semihosting as _; 9 | 10 | use cortex_m_semihosting::hprintln; 11 | use stm32f1xx_hal as _; 12 | 13 | use cortex_m_rt::entry; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | hprintln!("Hello, world!"); 18 | loop {} 19 | } 20 | -------------------------------------------------------------------------------- /examples/i2c-bme280/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "i2c-bme280" 3 | version = "0.1.0" 4 | license = "Apache-2.0" 5 | description = "I2C example for real peripheral" 6 | repository = "https://github.com/stm32-rs/stm32f1xx-hal" 7 | edition = "2018" 8 | publish = false 9 | 10 | [dependencies] 11 | bme280 = "0.3.0" 12 | cortex-m-semihosting = "0.5.0" 13 | panic-semihosting = "0.6.0" 14 | cortex-m-rt = "0.7.1" 15 | cortex-m = "0.7.6" 16 | 17 | [dependencies.stm32f1xx-hal] 18 | path = "../.." 19 | features = ["stm32f103", "stm32-usbd"] 20 | 21 | [profile.dev] 22 | incremental = false 23 | codegen-units = 1 24 | 25 | [profile.release] 26 | codegen-units = 1 27 | debug = true 28 | lto = true 29 | -------------------------------------------------------------------------------- /examples/i2c-bme280/memory.x: -------------------------------------------------------------------------------- 1 | /* Linker script for the STM32F103C8T6 */ 2 | MEMORY 3 | { 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 64K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 20K 6 | } 7 | -------------------------------------------------------------------------------- /examples/i2c-bme280/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Reads data from a BME280 over i2c 2 | //! 3 | //! This assumes that a BME280 is connected with clk on PB6 and data on PB7. 4 | //! 5 | //! For the Adafruit breakout boards PB6 should be connected to SCK and PB7 to SDI 6 | //! 7 | //! This program writes the sensor values to the debug output provided by semihosting 8 | //! you must enable semihosting in gdb with `monitor arm semihosting enable` I have it 9 | //! added to my `.gdbinit`. Then the debug infomation will be printed in your openocd 10 | //! terminal. 11 | //! 12 | //! This program dose not fit on my blue pill unless compiled in release mode 13 | //! eg. `cargo run --example i2c-bme280 --features "stm32f103 bme280 rt" --release` 14 | //! However as noted above the debug output with the read values will be in the openocd 15 | //! terminal. 16 | 17 | #![deny(unsafe_code)] 18 | #![no_std] 19 | #![no_main] 20 | 21 | use cortex_m_semihosting::hprintln; 22 | use panic_semihosting as _; 23 | 24 | use bme280::i2c::BME280; 25 | use cortex_m_rt::entry; 26 | use stm32f1xx_hal::{ 27 | i2c::{BlockingI2c, DutyCycle, Mode}, 28 | pac, 29 | prelude::*, 30 | }; 31 | 32 | #[entry] 33 | fn main() -> ! { 34 | // Get access to the core peripherals from the cortex-m crate 35 | let cp = cortex_m::Peripherals::take().unwrap(); 36 | // Get access to the device specific peripherals from the peripheral access crate 37 | let dp = pac::Peripherals::take().unwrap(); 38 | 39 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 40 | // HAL structs 41 | let mut flash = dp.FLASH.constrain(); 42 | let rcc = dp.RCC.constrain(); 43 | let mut afio = dp.AFIO.constrain(); 44 | // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 45 | // `clocks` 46 | let clocks = if 1 == 1 { 47 | rcc.cfgr.use_hse(8.MHz()).freeze(&mut flash.acr) 48 | } else { 49 | // My blue pill with a stm32f103 clone dose not seem to respect rcc so will not compensate its pulse legths 50 | // with a faster clock like this. And so the sensor dose not have time to respond to the START pulse. 51 | // I would be interested if others with real stm32f103's can use this program with the faster clocks. 52 | rcc.cfgr 53 | .use_hse(8.MHz()) 54 | .sysclk(48.MHz()) 55 | .pclk1(6.MHz()) 56 | .freeze(&mut flash.acr) 57 | }; 58 | 59 | // Acquire the GPIOB peripheral 60 | let mut gpiob = dp.GPIOB.split(); 61 | 62 | let scl = gpiob.pb6; 63 | let sda = gpiob.pb7; 64 | 65 | let i2c = dp 66 | .I2C1 67 | //.remap(&mut afio.mapr) // add this if want to use PB8, PB9 instead 68 | .blocking_i2c( 69 | (scl, sda), 70 | Mode::Fast { 71 | frequency: 400.kHz(), 72 | duty_cycle: DutyCycle::Ratio16to9, 73 | }, 74 | &clocks, 75 | 1000, 76 | 10, 77 | 1000, 78 | 1000, 79 | ); 80 | 81 | // The Adafruit boards have address 0x77 without closing the jumper on the back, the BME280 lib connects to 0x77 with `new_secondary`, use 82 | // `new_primary` for 0x76 if you close the jumper/solder bridge. 83 | let mut bme280 = BME280::new_secondary(i2c, cp.SYST.delay(&clocks)); 84 | bme280 85 | .init() 86 | .map_err(|error| { 87 | hprintln!("Could not initialize bme280, Error: {:?}", error); 88 | panic!(); 89 | }) 90 | .unwrap(); 91 | loop { 92 | match bme280.measure() { 93 | Ok(measurements) => { 94 | hprintln!("Relative Humidity = {}%", measurements.humidity); 95 | hprintln!("Temperature = {} deg C", measurements.temperature); 96 | hprintln!("Pressure = {} pascals", measurements.pressure) 97 | } 98 | Err(error) => { 99 | hprintln!("Could not read bme280 due to error: {:?}", error); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /examples/itm.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![allow(clippy::empty_loop)] 3 | #![no_main] 4 | #![no_std] 5 | 6 | use cortex_m::iprintln; 7 | use panic_itm as _; 8 | use stm32f1xx_hal as _; 9 | 10 | use cortex_m_rt::entry; 11 | 12 | #[entry] 13 | fn main() -> ! { 14 | let p = cortex_m::Peripherals::take().unwrap(); 15 | let mut itm = p.ITM; 16 | 17 | iprintln!(&mut itm.stim[0], "Hello, world!"); 18 | 19 | loop {} 20 | } 21 | -------------------------------------------------------------------------------- /examples/led.rs: -------------------------------------------------------------------------------- 1 | //! Turns the user LED on 2 | //! 3 | //! If compiled for the stm32f103, this assumes that an active low LED is connected to pc13 as 4 | //! is the case on the blue pill board. 5 | //! 6 | //! If compiled for the stm32f100, this assumes that an active high LED is connected to pc9 7 | //! 8 | //! Note: Without additional hardware, PC13 should not be used to drive a LED, see 9 | //! section 5.1.2 of the reference manual for an explanation. 10 | //! This is not an issue on the blue pill. 11 | 12 | #![allow(clippy::empty_loop)] 13 | #![deny(unsafe_code)] 14 | #![no_main] 15 | #![no_std] 16 | 17 | use panic_halt as _; 18 | 19 | use cortex_m_rt::entry; 20 | use stm32f1xx_hal::{pac, prelude::*}; 21 | 22 | #[entry] 23 | fn main() -> ! { 24 | let p = pac::Peripherals::take().unwrap(); 25 | 26 | let mut gpioc = p.GPIOC.split(); 27 | 28 | #[cfg(feature = "stm32f100")] 29 | gpioc.pc9.into_push_pull_output(&mut gpioc.crh).set_high(); 30 | 31 | #[cfg(feature = "stm32f101")] 32 | gpioc.pc9.into_push_pull_output(&mut gpioc.crh).set_high(); 33 | 34 | #[cfg(any(feature = "stm32f103", feature = "stm32f105", feature = "stm32f107"))] 35 | gpioc.pc13.into_push_pull_output(&mut gpioc.crh).set_low(); 36 | 37 | loop {} 38 | } 39 | -------------------------------------------------------------------------------- /examples/mfrc522.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![no_main] 3 | #![no_std] 4 | 5 | use panic_itm as _; 6 | 7 | use cortex_m::iprintln; 8 | 9 | use cortex_m_rt::entry; 10 | use mfrc522::{comm::eh02::spi::SpiInterface, Mfrc522}; 11 | use stm32f1xx_hal::{ 12 | pac, 13 | prelude::*, 14 | spi::{Mode, Phase, Polarity, Spi}, 15 | }; 16 | pub const MODE: Mode = Mode { 17 | polarity: Polarity::IdleLow, 18 | phase: Phase::CaptureOnFirstTransition, 19 | }; 20 | 21 | #[entry] 22 | fn main() -> ! { 23 | let mut cp = cortex_m::Peripherals::take().unwrap(); 24 | let dp = pac::Peripherals::take().unwrap(); 25 | 26 | let _stim = &mut cp.ITM.stim[0]; 27 | let rcc = dp.RCC.constrain(); 28 | let mut flash = dp.FLASH.constrain(); 29 | let mut gpioa = dp.GPIOA.split(); 30 | let mut gpioc = dp.GPIOC.split(); 31 | 32 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 33 | 34 | let sck = gpioa.pa5; 35 | let miso = gpioa.pa6; 36 | let mosi = gpioa.pa7; 37 | let spi = Spi::new( 38 | dp.SPI1, 39 | (Some(sck), Some(miso), Some(mosi)), 40 | MODE, 41 | 1.MHz(), 42 | &clocks, 43 | ); 44 | 45 | let nss = gpioa.pa4.into_push_pull_output(&mut gpioa.crl); 46 | let itf = SpiInterface::new(spi).with_nss(nss); 47 | let mut mfrc522 = Mfrc522::new(itf).init().unwrap(); 48 | 49 | let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 50 | led.set_high(); 51 | 52 | loop { 53 | if let Ok(atqa) = mfrc522.reqa() { 54 | if let Ok(uid) = mfrc522.select(&atqa) { 55 | iprintln!(_stim, "* {:?}", uid.as_bytes()); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/motor.rs.disabled: -------------------------------------------------------------------------------- 1 | //! Open loop motor control 2 | 3 | #![deny(unsafe_code)] 4 | #![deny(warnings)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | extern crate cortex_m_rt as rt; 9 | extern crate cortex_m_semihosting as sh; 10 | extern crate motor_driver; 11 | extern crate panic_semihosting; 12 | #[macro_use(block)] 13 | extern crate nb; 14 | extern crate stm32f1xx_hal as hal; 15 | 16 | use core::fmt::Write; 17 | 18 | use hal::prelude::*; 19 | use hal::serial::Serial; 20 | use hal::stm32f103xx; 21 | use motor_driver::Motor; 22 | use rt::{entry, exception, ExceptionFrame}; 23 | use sh::hio; 24 | 25 | #[entry] 26 | fn main() -> ! { 27 | let p = stm32f103xx::Peripherals::take().unwrap(); 28 | 29 | let mut flash = p.FLASH.constrain(); 30 | let mut rcc = p.RCC.constrain(); 31 | 32 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 33 | 34 | let mut afio = p.AFIO.constrain(); 35 | 36 | let mut gpioa = p.GPIOA.split(); 37 | 38 | let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh); 39 | let rx = gpioa.pa10; 40 | 41 | let serial = Serial::usart1( 42 | p.USART1, 43 | (tx, rx), 44 | &mut afio.mapr, 45 | 115_200.bps(), 46 | clocks, 47 | ); 48 | 49 | let mut rx = serial.split().1; 50 | 51 | let pwm = p.TIM2.pwm( 52 | gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl), 53 | &mut afio.mapr, 54 | 1.kHz(), 55 | clocks, 56 | ); 57 | 58 | let max_duty = pwm.get_max_duty() as i16; 59 | let mut motor = Motor::tb6612fng( 60 | gpioa.pa1.into_push_pull_output(&mut gpioa.crl), 61 | gpioa.pa2.into_push_pull_output(&mut gpioa.crl), 62 | pwm, 63 | ); 64 | 65 | let mut duty = max_duty; 66 | let mut brake = true; 67 | 68 | motor.duty(duty as u16); 69 | 70 | let mut hstdout = hio::hstdout().unwrap(); 71 | writeln!(hstdout, "{} {}", max_duty, brake).unwrap(); 72 | loop { 73 | match block!(rx.read()).unwrap() { 74 | b'*' => duty *= 2, 75 | b'+' => duty += 1, 76 | b'-' => duty -= 1, 77 | b'/' => duty /= 2, 78 | b'r' => duty *= -1, 79 | b's' => brake = !brake, 80 | _ => continue, 81 | } 82 | 83 | if duty > max_duty { 84 | duty = max_duty; 85 | } else if duty < -max_duty { 86 | duty = -max_duty; 87 | } 88 | 89 | if brake { 90 | motor.brake(); 91 | } else if duty > 0 { 92 | motor.cw(); 93 | } else { 94 | motor.ccw(); 95 | } 96 | 97 | motor.duty(duty.abs() as u16); 98 | 99 | writeln!(hstdout, "{} {}", duty, brake).unwrap(); 100 | } 101 | } 102 | 103 | #[exception] 104 | fn HardFault(ef: &ExceptionFrame) -> ! { 105 | panic!("{:#?}", ef); 106 | } 107 | 108 | #[exception] 109 | fn DefaultHandler(irqn: i16) { 110 | panic!("Unhandled exception (IRQn = {})", irqn); 111 | } 112 | -------------------------------------------------------------------------------- /examples/mpu9250.rs: -------------------------------------------------------------------------------- 1 | //! Interfacing the MPU9250 2 | 3 | #![deny(unsafe_code)] 4 | #![deny(warnings)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | use panic_halt as _; 9 | 10 | use cortex_m::asm; 11 | use cortex_m_rt::entry; 12 | use mpu9250::Mpu9250; 13 | use stm32f1xx_hal as hal; 14 | 15 | use hal::{pac, prelude::*, spi::Spi}; 16 | 17 | #[entry] 18 | fn main() -> ! { 19 | let cp = cortex_m::Peripherals::take().unwrap(); 20 | let dp = pac::Peripherals::take().unwrap(); 21 | 22 | let mut flash = dp.FLASH.constrain(); 23 | let rcc = dp.RCC.constrain(); 24 | 25 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 26 | 27 | let mut gpioa = dp.GPIOA.split(); 28 | // let mut gpiob = dp.GPIOB.split(); 29 | 30 | let nss = gpioa.pa4.into_push_pull_output(&mut gpioa.crl); 31 | 32 | // SPI1 33 | let sck = gpioa.pa5; 34 | let miso = gpioa.pa6; 35 | let mosi = gpioa.pa7; 36 | 37 | // SPI2 38 | // let sck = gpiob.pb13; 39 | // let miso = gpiob.pb14; 40 | // let mosi = gpiob.pb15; 41 | 42 | let spi = Spi::new( 43 | dp.SPI1, 44 | (Some(sck), Some(miso), Some(mosi)), 45 | mpu9250::MODE.into(), 46 | 1.MHz(), 47 | &clocks, 48 | ); 49 | 50 | let mut delay = cp.SYST.delay(&clocks); 51 | 52 | let mut mpu9250 = Mpu9250::marg_default(spi, nss, &mut delay).unwrap(); 53 | 54 | // sanity checks 55 | assert_eq!(mpu9250.who_am_i().unwrap(), 0x71); 56 | assert_eq!(mpu9250.ak8963_who_am_i().unwrap(), 0x48); 57 | 58 | let _a = mpu9250.all::<[f32; 3]>().unwrap(); 59 | 60 | asm::bkpt(); 61 | 62 | loop {} 63 | } 64 | -------------------------------------------------------------------------------- /examples/multi_mode_gpio.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![no_std] 3 | #![no_main] 4 | 5 | use panic_halt as _; 6 | 7 | use nb::block; 8 | 9 | use cortex_m_rt::entry; 10 | use cortex_m_semihosting::hprintln; 11 | use stm32f1xx_hal::{gpio::PinState, pac, prelude::*, timer::Timer}; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | // Get access to the core peripherals from the cortex-m crate 16 | let cp = cortex_m::Peripherals::take().unwrap(); 17 | // Get access to the device specific peripherals from the peripheral access crate 18 | let dp = pac::Peripherals::take().unwrap(); 19 | 20 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 21 | // HAL structs 22 | let mut flash = dp.FLASH.constrain(); 23 | let rcc = dp.RCC.constrain(); 24 | 25 | // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 26 | // `clocks` 27 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 28 | 29 | // Acquire the GPIOC peripheral 30 | let mut gpioc = dp.GPIOC.split(); 31 | 32 | let mut pin = gpioc.pc13.into_floating_input(&mut gpioc.crh); 33 | // Configure the syst timer to trigger an update every second 34 | let mut timer = Timer::syst(cp.SYST, &clocks).counter_hz(); 35 | timer.start(1.Hz()).unwrap(); 36 | 37 | // Wait for the timer to trigger an update and change the state of the LED 38 | loop { 39 | block!(timer.wait()).unwrap(); 40 | hprintln!("{}", pin.is_high()); 41 | pin.as_push_pull_output(&mut gpioc.crh, |out| { 42 | out.set_high(); 43 | block!(timer.wait()).unwrap(); 44 | out.set_low(); 45 | block!(timer.wait()).unwrap(); 46 | }); 47 | pin.as_push_pull_output_with_state(&mut gpioc.crh, PinState::High, |out| { 48 | block!(timer.wait()).unwrap(); 49 | out.set_low(); 50 | block!(timer.wait()).unwrap(); 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/nojtag.rs: -------------------------------------------------------------------------------- 1 | //! Disables the JTAG ports to give access to pb3, pb4 and PA15 2 | 3 | #![deny(unsafe_code)] 4 | #![no_main] 5 | #![no_std] 6 | 7 | use panic_halt as _; 8 | 9 | use cortex_m_rt::entry; 10 | use stm32f1xx_hal::{pac, prelude::*}; 11 | 12 | #[entry] 13 | fn main() -> ! { 14 | let p = pac::Peripherals::take().unwrap(); 15 | 16 | let mut gpioa = p.GPIOA.split(); 17 | let mut gpiob = p.GPIOB.split(); 18 | let mut afio = p.AFIO.constrain(); 19 | 20 | let (pa15, pb3, pb4) = afio.mapr.disable_jtag(gpioa.pa15, gpiob.pb3, gpiob.pb4); 21 | 22 | let mut pa15 = pa15.into_push_pull_output(&mut gpioa.crh); 23 | let mut pb3 = pb3.into_push_pull_output(&mut gpiob.crl); 24 | let mut pb4 = pb4.into_push_pull_output(&mut gpiob.crl); 25 | 26 | loop { 27 | pa15.toggle(); 28 | pb3.toggle(); 29 | pb4.toggle(); 30 | cortex_m::asm::delay(8_000_000); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/panics.rs: -------------------------------------------------------------------------------- 1 | //! Prints "Hello, world" on the OpenOCD console 2 | 3 | #![allow(clippy::empty_loop)] 4 | #![no_main] 5 | #![no_std] 6 | 7 | use panic_semihosting as _; 8 | //use panic_itm as _; 9 | use cortex_m_semihosting::hprintln; 10 | use stm32f1xx_hal as _; 11 | 12 | use cortex_m_rt::{entry, exception, ExceptionFrame}; 13 | 14 | #[entry] 15 | fn main() -> ! { 16 | hprintln!("Hello, world!"); 17 | loop {} 18 | } 19 | 20 | #[exception] 21 | unsafe fn HardFault(ef: &ExceptionFrame) -> ! { 22 | panic!("{:#?}", ef); 23 | } 24 | 25 | #[exception] 26 | unsafe fn DefaultHandler(irqn: i16) { 27 | panic!("Unhandled exception (IRQn = {})", irqn); 28 | } 29 | -------------------------------------------------------------------------------- /examples/pwm.rs: -------------------------------------------------------------------------------- 1 | //! Testing PWM output for pre-defined pin combination: all pins for default mapping 2 | 3 | #![deny(unsafe_code)] 4 | #![allow(clippy::empty_loop)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | use panic_halt as _; 9 | 10 | use cortex_m::asm; 11 | use cortex_m_rt::entry; 12 | use stm32f1xx_hal::{ 13 | pac, 14 | prelude::*, 15 | time::ms, 16 | timer::{Channel, Tim2NoRemap}, 17 | }; 18 | 19 | #[entry] 20 | fn main() -> ! { 21 | let p = pac::Peripherals::take().unwrap(); 22 | 23 | let mut flash = p.FLASH.constrain(); 24 | let rcc = p.RCC.constrain(); 25 | 26 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 27 | 28 | let mut afio = p.AFIO.constrain(); 29 | 30 | let mut gpioa = p.GPIOA.split(); 31 | // let mut gpiob = p.GPIOB.split(); 32 | 33 | // TIM2 34 | let c1 = gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl); 35 | let c2 = gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl); 36 | let c3 = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); 37 | // If you don't want to use all channels, just leave some out 38 | // let c4 = gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl); 39 | let pins = (c1, c2, c3); 40 | 41 | // TIM3 42 | // let c1 = gpioa.pa6.into_alternate_push_pull(&mut gpioa.crl); 43 | // let c2 = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl); 44 | // let c3 = gpiob.pb0.into_alternate_push_pull(&mut gpiob.crl); 45 | // let c4 = gpiob.pb1.into_alternate_push_pull(&mut gpiob.crl); 46 | 47 | // TIM4 (Only available with the "medium" density feature) 48 | // let c1 = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); 49 | // let c2 = gpiob.pb7.into_alternate_push_pull(&mut gpiob.crl); 50 | // let c3 = gpiob.pb8.into_alternate_push_pull(&mut gpiob.crh); 51 | // let c4 = gpiob.pb9.into_alternate_push_pull(&mut gpiob.crh); 52 | 53 | //let mut pwm = 54 | // Timer::new(p.TIM2, &clocks).pwm_hz::(pins, &mut afio.mapr, 1.kHz()); 55 | // or 56 | let mut pwm = p 57 | .TIM2 58 | .pwm_hz::(pins, &mut afio.mapr, 1.kHz(), &clocks); 59 | 60 | // Enable clock on each of the channels 61 | pwm.enable(Channel::C1); 62 | pwm.enable(Channel::C2); 63 | pwm.enable(Channel::C3); 64 | 65 | //// Operations affecting all defined channels on the Timer 66 | 67 | // Adjust period to 0.5 seconds 68 | pwm.set_period(ms(500).into_rate()); 69 | 70 | asm::bkpt(); 71 | 72 | // Return to the original frequency 73 | pwm.set_period(1.kHz()); 74 | 75 | asm::bkpt(); 76 | 77 | let max = pwm.get_max_duty(); 78 | 79 | //// Operations affecting single channels can be accessed through 80 | //// the Pwm object or via dereferencing to the pin. 81 | 82 | // Use the Pwm object to set C3 to full strength 83 | pwm.set_duty(Channel::C3, max); 84 | 85 | asm::bkpt(); 86 | 87 | // Use the Pwm object to set C3 to be dim 88 | pwm.set_duty(Channel::C3, max / 4); 89 | 90 | asm::bkpt(); 91 | 92 | // Use the Pwm object to set C3 to be zero 93 | pwm.set_duty(Channel::C3, 0); 94 | 95 | asm::bkpt(); 96 | 97 | // Extract the PwmChannel for C3 98 | let mut pwm_channel = pwm.split().2; 99 | 100 | // Use the PwmChannel object to set C3 to be full strength 101 | pwm_channel.set_duty(max); 102 | 103 | asm::bkpt(); 104 | 105 | // Use the PwmChannel object to set C3 to be dim 106 | pwm_channel.set_duty(max / 4); 107 | 108 | asm::bkpt(); 109 | 110 | // Use the PwmChannel object to set C3 to be zero 111 | pwm_channel.set_duty(0); 112 | 113 | asm::bkpt(); 114 | 115 | loop {} 116 | } 117 | -------------------------------------------------------------------------------- /examples/pwm_custom.rs: -------------------------------------------------------------------------------- 1 | //! Testing PWM output for custom pin combinations 2 | 3 | #![deny(unsafe_code)] 4 | #![allow(clippy::empty_loop)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | use panic_halt as _; 9 | 10 | use cortex_m::asm; 11 | use stm32f1xx_hal::{pac, prelude::*, timer::Timer}; 12 | 13 | use cortex_m_rt::entry; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | let p = pac::Peripherals::take().unwrap(); 18 | 19 | let mut flash = p.FLASH.constrain(); 20 | let rcc = p.RCC.constrain(); 21 | 22 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 23 | 24 | let mut afio = p.AFIO.constrain(); 25 | let gpioa = p.GPIOA.split(); 26 | let mut gpiob = p.GPIOB.split(); 27 | let (_pa15, _pb3, pb4) = afio.mapr.disable_jtag(gpioa.pa15, gpiob.pb3, gpiob.pb4); 28 | 29 | // TIM3 30 | let p0 = pb4.into_alternate_push_pull(&mut gpiob.crl); 31 | let p1 = gpiob.pb5.into_alternate_push_pull(&mut gpiob.crl); 32 | 33 | let pwm = Timer::new(p.TIM3, &clocks).pwm_hz((p0, p1), &mut afio.mapr, 1.kHz()); 34 | 35 | let max = pwm.get_max_duty(); 36 | 37 | let mut pwm_channels = pwm.split(); 38 | 39 | // Enable the individual channels 40 | pwm_channels.0.enable(); 41 | pwm_channels.1.enable(); 42 | 43 | // full 44 | pwm_channels.0.set_duty(max); 45 | pwm_channels.1.set_duty(max); 46 | 47 | asm::bkpt(); 48 | 49 | // dim 50 | pwm_channels.1.set_duty(max / 4); 51 | 52 | asm::bkpt(); 53 | 54 | // zero 55 | pwm_channels.0.set_duty(0); 56 | pwm_channels.1.set_duty(0); 57 | 58 | asm::bkpt(); 59 | 60 | loop {} 61 | } 62 | -------------------------------------------------------------------------------- /examples/pwm_input.rs: -------------------------------------------------------------------------------- 1 | //! Testing PWM input 2 | 3 | #![deny(unsafe_code)] 4 | #![no_main] 5 | #![no_std] 6 | 7 | use panic_halt as _; 8 | 9 | use cortex_m_rt::entry; 10 | use stm32f1xx_hal::{pac, prelude::*, timer::pwm_input::*}; 11 | 12 | #[entry] 13 | fn main() -> ! { 14 | let p = pac::Peripherals::take().unwrap(); 15 | 16 | let mut flash = p.FLASH.constrain(); 17 | let rcc = p.RCC.constrain(); 18 | 19 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 20 | 21 | let mut afio = p.AFIO.constrain(); 22 | let mut dbg = p.DBGMCU; 23 | 24 | let gpioa = p.GPIOA.split(); 25 | let gpiob = p.GPIOB.split(); 26 | 27 | let (_pa15, _pb3, pb4) = afio.mapr.disable_jtag(gpioa.pa15, gpiob.pb3, gpiob.pb4); 28 | let pb5 = gpiob.pb5; 29 | 30 | let pwm_input = p.TIM3.remap(&mut afio.mapr).pwm_input( 31 | (pb4, pb5), 32 | &mut dbg, 33 | Configuration::Frequency(10.kHz()), 34 | &clocks, 35 | ); 36 | 37 | loop { 38 | let _freq = pwm_input 39 | .read_frequency(ReadMode::Instant, &clocks) 40 | .unwrap(); 41 | let _duty_cycle = pwm_input.read_duty(ReadMode::Instant).unwrap(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/qei.rs: -------------------------------------------------------------------------------- 1 | //! Testing the Quadrature Encoder Interface 2 | 3 | #![deny(unsafe_code)] 4 | #![no_main] 5 | #![no_std] 6 | 7 | use panic_semihosting as _; 8 | 9 | use cortex_m_semihosting::hprintln; 10 | 11 | use cortex_m_rt::entry; 12 | use stm32f1xx_hal::{ 13 | pac, 14 | prelude::*, 15 | timer::{pwm_input::QeiOptions, Timer}, 16 | }; 17 | 18 | #[entry] 19 | fn main() -> ! { 20 | let dp = pac::Peripherals::take().unwrap(); 21 | let cp = cortex_m::Peripherals::take().unwrap(); 22 | 23 | let mut flash = dp.FLASH.constrain(); 24 | let rcc = dp.RCC.constrain(); 25 | 26 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 27 | 28 | // let gpioa = dp.GPIOA.split(); 29 | let gpiob = dp.GPIOB.split(); 30 | 31 | // TIM2 32 | // let c1 = gpioa.pa0; 33 | // let c2 = gpioa.pa1; 34 | 35 | // TIM3 36 | // let c1 = gpioa.pa6; 37 | // let c2 = gpioa.pa7; 38 | 39 | // TIM4 40 | let c1 = gpiob.pb6; 41 | let c2 = gpiob.pb7; 42 | 43 | let qei = Timer::new(dp.TIM4, &clocks).qei((c1, c2), QeiOptions::default()); 44 | let mut delay = cp.SYST.delay(&clocks); 45 | 46 | loop { 47 | let before = qei.count(); 48 | delay.delay_ms(1_000_u16); 49 | let after = qei.count(); 50 | 51 | let elapsed = after.wrapping_sub(before) as i16; 52 | 53 | hprintln!("{}", elapsed); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/rtc.rs: -------------------------------------------------------------------------------- 1 | //! Outputs the current time in seconds to hstdout using the real time clock 2 | 3 | #![deny(unsafe_code)] 4 | #![no_std] 5 | #![no_main] 6 | 7 | use panic_semihosting as _; 8 | 9 | use cortex_m_semihosting::hprintln; 10 | 11 | use cortex_m_rt::entry; 12 | use fugit::RateExtU32; 13 | use stm32f1xx_hal::rtc::RestoredOrNewRtc::{New, Restored}; 14 | use stm32f1xx_hal::{pac, prelude::*, rtc::Rtc}; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | let p = pac::Peripherals::take().unwrap(); 19 | 20 | let mut pwr = p.PWR; 21 | let rcc = p.RCC.constrain(); 22 | let mut backup_domain = rcc.bkp.constrain(p.BKP, &mut pwr); 23 | 24 | // Initializes rtc every startup, use only if you don't have a battery. 25 | // let rtc = Rtc::new(p.RTC, &mut backup_domain); 26 | 27 | // Restores Rtc: that happens in case it was already running, a battery is connected, 28 | // and it was already initialized before. 29 | // If you are going to use ::new with battery, the time will lack behind 30 | // due to unnecessary reinitialization of the crystal, 31 | // as well as reset of the selected frequency. 32 | // Else, the rtc is initialized. 33 | let rtc = match Rtc::restore_or_new(p.RTC, &mut backup_domain) { 34 | Restored(rtc) => rtc, // The rtc is restored from previous configuration. You may verify the frequency you want if needed. 35 | New(mut rtc) => { 36 | // The rtc was just initialized, the clock source selected, frequency is 1.Hz() 37 | // Initialize rtc with desired parameters 38 | rtc.select_frequency(2u32.Hz()); // Set the frequency to 2 Hz. This will stay same after reset 39 | rtc 40 | } 41 | }; 42 | 43 | loop { 44 | hprintln!("time: {}", rtc.current_time()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/serial-dma-circ.rs: -------------------------------------------------------------------------------- 1 | //! Serial interface circular DMA RX transfer test 2 | 3 | #![deny(unsafe_code)] 4 | #![allow(clippy::empty_loop)] 5 | #![no_std] 6 | #![no_main] 7 | 8 | use panic_halt as _; 9 | 10 | use cortex_m::{asm, singleton}; 11 | 12 | use cortex_m_rt::entry; 13 | use stm32f1xx_hal::{ 14 | dma::Half, 15 | pac, 16 | prelude::*, 17 | serial::{Config, Serial}, 18 | }; 19 | 20 | #[entry] 21 | fn main() -> ! { 22 | let p = pac::Peripherals::take().unwrap(); 23 | 24 | let mut flash = p.FLASH.constrain(); 25 | let rcc = p.RCC.constrain(); 26 | 27 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 28 | 29 | //let mut afio = p.AFIO.constrain(); 30 | let channels = p.DMA1.split(); 31 | 32 | let mut gpioa = p.GPIOA.split(); 33 | // let mut gpiob = p.GPIOB.split(); 34 | 35 | // USART1 36 | let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh); 37 | let rx = gpioa.pa10; 38 | 39 | // USART1 40 | // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); 41 | // let rx = gpiob.pb7; 42 | 43 | // USART2 44 | // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); 45 | // let rx = gpioa.pa3; 46 | 47 | // USART3 48 | // let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); 49 | // let rx = gpiob.pb11; 50 | 51 | let serial = Serial::new( 52 | p.USART1, 53 | (tx, rx), 54 | Config::default().baudrate(9_600.bps()), 55 | &clocks, 56 | ); 57 | 58 | let rx = serial.rx.with_dma(channels.5); 59 | let buf = singleton!(: [[u8; 8]; 2] = [[0; 8]; 2]).unwrap(); 60 | 61 | let mut circ_buffer = rx.circ_read(buf); 62 | 63 | while circ_buffer.readable_half().unwrap() != Half::First {} 64 | 65 | let _first_half = circ_buffer.peek(|half, _| *half).unwrap(); 66 | 67 | while circ_buffer.readable_half().unwrap() != Half::Second {} 68 | 69 | let _second_half = circ_buffer.peek(|half, _| *half).unwrap(); 70 | 71 | asm::bkpt(); 72 | 73 | loop {} 74 | } 75 | -------------------------------------------------------------------------------- /examples/serial-dma-peek.rs: -------------------------------------------------------------------------------- 1 | //! Serial interface DMA RX transfer test 2 | 3 | #![allow(clippy::empty_loop)] 4 | #![deny(unsafe_code)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | use panic_halt as _; 9 | 10 | use cortex_m::{asm, singleton}; 11 | 12 | use cortex_m_rt::entry; 13 | use stm32f1xx_hal::{ 14 | pac, 15 | prelude::*, 16 | serial::{Config, Serial}, 17 | }; 18 | 19 | #[entry] 20 | fn main() -> ! { 21 | let p = pac::Peripherals::take().unwrap(); 22 | 23 | let mut flash = p.FLASH.constrain(); 24 | let rcc = p.RCC.constrain(); 25 | 26 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 27 | 28 | //let mut afio = p.AFIO.constrain(); 29 | let channels = p.DMA1.split(); 30 | 31 | let mut gpioa = p.GPIOA.split(); 32 | // let mut gpiob = p.GPIOB.split(); 33 | 34 | // USART1 35 | let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh); 36 | let rx = gpioa.pa10; 37 | 38 | // USART1 39 | // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); 40 | // let rx = gpiob.pb7; 41 | 42 | // USART2 43 | // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); 44 | // let rx = gpioa.pa3; 45 | 46 | // USART3 47 | // let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); 48 | // let rx = gpiob.pb11; 49 | 50 | let serial = Serial::new(p.USART1, (tx, rx), Config::default(), &clocks); 51 | 52 | let rx = serial.rx.with_dma(channels.5); 53 | let buf = singleton!(: [u8; 8] = [0; 8]).unwrap(); 54 | 55 | let t = rx.read(buf); 56 | 57 | while !t.is_done() { 58 | let _slice = t.peek(); 59 | 60 | asm::bkpt(); 61 | } 62 | 63 | asm::bkpt(); 64 | 65 | loop {} 66 | } 67 | -------------------------------------------------------------------------------- /examples/serial-dma-rx.rs: -------------------------------------------------------------------------------- 1 | //! Serial interface DMA RX transfer test 2 | 3 | #![deny(unsafe_code)] 4 | #![allow(clippy::empty_loop)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | use panic_halt as _; 9 | 10 | use cortex_m::{asm, singleton}; 11 | 12 | use cortex_m_rt::entry; 13 | use stm32f1xx_hal::{ 14 | pac, 15 | prelude::*, 16 | serial::{Config, Serial}, 17 | }; 18 | 19 | #[entry] 20 | fn main() -> ! { 21 | let p = pac::Peripherals::take().unwrap(); 22 | 23 | let mut flash = p.FLASH.constrain(); 24 | let rcc = p.RCC.constrain(); 25 | 26 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 27 | 28 | //let mut afio = p.AFIO.constrain(); 29 | let channels = p.DMA1.split(); 30 | 31 | let mut gpioa = p.GPIOA.split(); 32 | // let mut gpiob = p.GPIOB.split(); 33 | 34 | // USART1 35 | let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh); 36 | let rx = gpioa.pa10; 37 | 38 | // USART1 39 | // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); 40 | // let rx = gpiob.pb7; 41 | 42 | // USART2 43 | // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); 44 | // let rx = gpioa.pa3; 45 | 46 | // USART3 47 | // let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); 48 | // let rx = gpiob.pb11; 49 | 50 | let serial = Serial::new( 51 | p.USART1, 52 | (tx, rx), 53 | Config::default().baudrate(9_600.bps()), 54 | &clocks, 55 | ); 56 | 57 | let rx = serial.rx.with_dma(channels.5); 58 | let buf = singleton!(: [u8; 8] = [0; 8]).unwrap(); 59 | 60 | let (_buf, _rx) = rx.read(buf).wait(); 61 | 62 | asm::bkpt(); 63 | 64 | loop {} 65 | } 66 | -------------------------------------------------------------------------------- /examples/serial-dma-tx.rs: -------------------------------------------------------------------------------- 1 | //! Serial interface DMA TX transfer test 2 | 3 | #![deny(unsafe_code)] 4 | #![allow(clippy::empty_loop)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | use panic_halt as _; 9 | 10 | use cortex_m::asm; 11 | 12 | use cortex_m_rt::entry; 13 | use stm32f1xx_hal::{ 14 | pac, 15 | prelude::*, 16 | serial::{Config, Serial}, 17 | }; 18 | 19 | #[entry] 20 | fn main() -> ! { 21 | let p = pac::Peripherals::take().unwrap(); 22 | 23 | let mut flash = p.FLASH.constrain(); 24 | let rcc = p.RCC.constrain(); 25 | 26 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 27 | 28 | //let mut afio = p.AFIO.constrain(); 29 | let channels = p.DMA1.split(); 30 | 31 | let mut gpioa = p.GPIOA.split(); 32 | // let mut gpiob = p.GPIOB.split(); 33 | 34 | // USART1 35 | let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh); 36 | let rx = gpioa.pa10; 37 | 38 | // USART1 39 | // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); 40 | // let rx = gpiob.pb7; 41 | 42 | // USART2 43 | // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); 44 | // let rx = gpioa.pa3; 45 | 46 | // USART3 47 | // let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); 48 | // let rx = gpiob.pb11; 49 | 50 | let serial = Serial::new( 51 | p.USART1, 52 | (tx, rx), 53 | Config::default().baudrate(9600.bps()), 54 | &clocks, 55 | ); 56 | 57 | let tx = serial.tx.with_dma(channels.4); 58 | 59 | let (_, tx) = tx.write(b"The quick brown fox").wait(); 60 | 61 | asm::bkpt(); 62 | 63 | let (_, tx) = tx.write(b" jumps").wait(); 64 | 65 | asm::bkpt(); 66 | 67 | tx.write(b" over the lazy dog.").wait(); 68 | 69 | asm::bkpt(); 70 | 71 | loop {} 72 | } 73 | -------------------------------------------------------------------------------- /examples/serial-fmt.rs: -------------------------------------------------------------------------------- 1 | //! Serial interface write formatted strings test 2 | //! 3 | //! You need to connect the Tx pin to the Rx pin of a serial-usb converter 4 | //! so you can see the message in a serial console (e.g. Arduino console). 5 | 6 | #![deny(unsafe_code)] 7 | #![allow(clippy::empty_loop)] 8 | #![no_main] 9 | #![no_std] 10 | 11 | use panic_halt as _; 12 | 13 | use cortex_m_rt::entry; 14 | use stm32f1xx_hal::{ 15 | pac, 16 | prelude::*, 17 | serial::{Config, Serial}, 18 | }; 19 | 20 | use core::fmt::Write; 21 | 22 | #[entry] 23 | fn main() -> ! { 24 | // Get access to the device specific peripherals from the peripheral access crate 25 | let p = pac::Peripherals::take().unwrap(); 26 | 27 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 28 | // HAL structs 29 | let mut flash = p.FLASH.constrain(); 30 | let rcc = p.RCC.constrain(); 31 | 32 | // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 33 | // `clocks` 34 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 35 | 36 | // Prepare the alternate function I/O registers 37 | //let mut afio = p.AFIO.constrain(); 38 | 39 | // Prepare the GPIOB peripheral 40 | let mut gpiob = p.GPIOB.split(); 41 | 42 | // USART1 43 | // let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh); 44 | // let rx = gpioa.pa10; 45 | 46 | // USART1 47 | // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); 48 | // let rx = gpiob.pb7; 49 | 50 | // USART2 51 | // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); 52 | // let rx = gpioa.pa3; 53 | 54 | // USART3 55 | // Configure pb10 as a push_pull output, this will be the tx pin 56 | let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); 57 | // Take ownership over pb11 58 | let rx = gpiob.pb11; 59 | 60 | // Set up the usart device. Take ownership over the USART register and tx/rx pins. The rest of 61 | // the registers are used to enable and configure the device. 62 | let serial = Serial::new( 63 | p.USART3, 64 | (tx, rx), 65 | Config::default().baudrate(9600.bps()), 66 | &clocks, 67 | ); 68 | 69 | // Split the serial struct into a receiving and a transmitting part 70 | let (mut tx, _rx) = serial.split(); 71 | 72 | let number = 103; 73 | writeln!(tx, "Hello formatted string {}", number).unwrap(); 74 | 75 | // for windows 76 | // write!(tx, "Hello formatted string {}\r\n", number).unwrap(); 77 | 78 | loop {} 79 | } 80 | -------------------------------------------------------------------------------- /examples/serial-interrupt-idle.rs: -------------------------------------------------------------------------------- 1 | //! Serial interface loopback test 2 | //! 3 | //! You have to short the TX and RX pins to make this program work 4 | 5 | #![no_main] 6 | #![no_std] 7 | 8 | use panic_halt as _; 9 | 10 | use cortex_m_rt::entry; 11 | use stm32f1xx_hal::{ 12 | pac::{self, interrupt, USART1}, 13 | prelude::*, 14 | serial::{Rx, Tx}, 15 | }; 16 | 17 | static mut RX: Option> = None; 18 | static mut TX: Option> = None; 19 | #[entry] 20 | fn main() -> ! { 21 | // Get access to the device specific peripherals from the peripheral access crate 22 | let p = pac::Peripherals::take().unwrap(); 23 | 24 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 25 | // HAL structs 26 | let mut flash = p.FLASH.constrain(); 27 | let rcc = p.RCC.constrain(); 28 | 29 | // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 30 | // `clocks` 31 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 32 | 33 | // Prepare the alternate function I/O registers 34 | let mut afio = p.AFIO.constrain(); 35 | 36 | // Prepare the GPIOB peripheral 37 | let mut gpiob = p.GPIOB.split(); 38 | 39 | // USART1 40 | let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); 41 | let rx = gpiob.pb7; 42 | 43 | // Set up the usart device. Takes ownership over the USART register and tx/rx pins. The rest of 44 | // the registers are used to enable and configure the device. 45 | let (mut tx, mut rx) = p 46 | .USART1 47 | .remap(&mut afio.mapr) 48 | .serial((tx, rx), 115_200.bps(), &clocks) 49 | .split(); 50 | tx.listen(); 51 | rx.listen(); 52 | rx.listen_idle(); 53 | 54 | cortex_m::interrupt::free(|_| unsafe { 55 | TX.replace(tx); 56 | RX.replace(rx); 57 | }); 58 | unsafe { 59 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::USART1); 60 | } 61 | 62 | loop { 63 | cortex_m::asm::wfi() 64 | } 65 | } 66 | const BUFFER_LEN: usize = 4096; 67 | static mut BUFFER: &mut [u8; BUFFER_LEN] = &mut [0; BUFFER_LEN]; 68 | static mut WIDX: usize = 0; 69 | 70 | unsafe fn write(buf: &[u8]) { 71 | if let Some(tx) = TX.as_mut() { 72 | buf.iter() 73 | .for_each(|w| if let Err(_err) = nb::block!(tx.write(*w)) {}) 74 | } 75 | } 76 | #[interrupt] 77 | unsafe fn USART1() { 78 | cortex_m::interrupt::free(|_| { 79 | if let Some(rx) = RX.as_mut() { 80 | if rx.is_rx_not_empty() { 81 | if let Ok(w) = nb::block!(rx.read()) { 82 | BUFFER[WIDX] = w; 83 | WIDX += 1; 84 | if WIDX >= BUFFER_LEN - 1 { 85 | write(&BUFFER[..]); 86 | WIDX = 0; 87 | } 88 | } 89 | rx.listen_idle(); 90 | } else if rx.is_idle() { 91 | rx.unlisten_idle(); 92 | write(&BUFFER[0..WIDX]); 93 | WIDX = 0; 94 | } 95 | } 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /examples/serial.rs: -------------------------------------------------------------------------------- 1 | //! Serial interface loopback test 2 | //! 3 | //! You have to short the TX and RX pins to make this program work 4 | 5 | #![allow(clippy::empty_loop)] 6 | #![deny(unsafe_code)] 7 | #![no_main] 8 | #![no_std] 9 | 10 | use panic_halt as _; 11 | 12 | use cortex_m::asm; 13 | 14 | use nb::block; 15 | 16 | use cortex_m_rt::entry; 17 | use stm32f1xx_hal::{pac, prelude::*, serial::Config}; 18 | 19 | #[entry] 20 | fn main() -> ! { 21 | // Get access to the device specific peripherals from the peripheral access crate 22 | let p = pac::Peripherals::take().unwrap(); 23 | 24 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 25 | // HAL structs 26 | let mut flash = p.FLASH.constrain(); 27 | let rcc = p.RCC.constrain(); 28 | 29 | // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 30 | // `clocks` 31 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 32 | 33 | // Prepare the alternate function I/O registers 34 | //let mut afio = p.AFIO.constrain(); 35 | 36 | // Prepare the GPIOB peripheral 37 | let mut gpiob = p.GPIOB.split(); 38 | 39 | // USART1 40 | // let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh); 41 | // let rx = gpioa.pa10; 42 | 43 | // USART1 44 | // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); 45 | // let rx = gpiob.pb7; 46 | 47 | // USART2 48 | // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); 49 | // let rx = gpioa.pa3; 50 | 51 | // USART3 52 | // Configure pb10 as a push_pull output, this will be the tx pin 53 | let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); 54 | // Take ownership over pb11 55 | let rx = gpiob.pb11; 56 | 57 | // Set up the usart device. Take ownership over the USART register and tx/rx pins. The rest of 58 | // the registers are used to enable and configure the device. 59 | let mut serial = p 60 | .USART3 61 | .serial((tx, rx), Config::default().baudrate(115200.bps()), &clocks); 62 | 63 | // Loopback test. Write `X` and wait until the write is successful. 64 | let sent = b'X'; 65 | block!(serial.tx.write_u8(sent)).unwrap(); 66 | 67 | // Read the byte that was just sent. Blocks until the read is complete 68 | let received = block!(serial.rx.read()).unwrap(); 69 | 70 | // Since we have connected tx and rx, the byte we sent should be the one we received 71 | assert_eq!(received, sent); 72 | 73 | // Trigger a breakpoint to allow us to inspect the values 74 | asm::bkpt(); 75 | 76 | // You can also split the serial struct into a receiving and a transmitting part 77 | let (mut tx, mut rx) = serial.split(); 78 | let received = block!(rx.read()).unwrap(); 79 | //let sent = b'Y'; 80 | block!(tx.write_u8(received)).unwrap(); 81 | //assert_eq!(received, sent); 82 | asm::bkpt(); 83 | 84 | loop {} 85 | } 86 | -------------------------------------------------------------------------------- /examples/serial_9bits.rs: -------------------------------------------------------------------------------- 1 | //! Testing 9 bits USART word length mode. 2 | //! 3 | //! This example demonstrates the use of the MSB bit (bit 8) to mark the beginning of a packet. 4 | //! The first byte of the packet contains the address of the slave device. 5 | //! The second byte of the packet contains the length of the message. 6 | //! The remaining bytes of the packet contain the message itself. 7 | 8 | //#![deny(unsafe_code)] 9 | #![no_main] 10 | #![no_std] 11 | 12 | use cortex_m_rt::entry; 13 | use nb::block; 14 | use panic_halt as _; 15 | use stm32f1xx_hal::{ 16 | gpio::PushPull, 17 | pac, 18 | prelude::*, 19 | serial::{self, Config, Error}, 20 | }; 21 | 22 | // The address of the slave device. 23 | const SLAVE_ADDR: u8 = 123; 24 | 25 | // Maximum possible message length. 26 | const MSG_MAX_LEN: usize = u8::MAX as usize; 27 | 28 | // Receives a message addressed to the slave device. Returns the size of the received message. 29 | fn receive_msg(serial_rx: &mut RX, buf: &mut [u8; MSG_MAX_LEN]) -> usize 30 | where 31 | RX: embedded_hal_02::serial::Read, 32 | { 33 | enum RxPhase { 34 | Start, 35 | Length, 36 | Data { len: usize, idx: usize }, 37 | } 38 | 39 | let mut rx_phase = RxPhase::Start; 40 | 41 | loop { 42 | // Read the word that was just sent. Blocks until the read is complete. 43 | let received = block!(serial_rx.read()).unwrap(); 44 | 45 | // If the beginning of the packet. 46 | if (received & 0x100) != 0 { 47 | rx_phase = if received as u8 == SLAVE_ADDR { 48 | RxPhase::Length 49 | } else { 50 | RxPhase::Start 51 | } 52 | } else { 53 | match rx_phase { 54 | RxPhase::Start => {} 55 | 56 | RxPhase::Length => { 57 | if received == 0 { 58 | return 0; 59 | } 60 | rx_phase = RxPhase::Data { 61 | len: received as usize, 62 | idx: 0, 63 | }; 64 | } 65 | 66 | RxPhase::Data { len, ref mut idx } => { 67 | buf[*idx] = received as u8; 68 | *idx += 1; 69 | if *idx == len { 70 | return len; 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | 78 | // Send message. 79 | fn send_msg(serial_tx: &mut TX, msg: &[u8]) 80 | where 81 | TX: embedded_hal_02::serial::Write 82 | + embedded_hal_02::serial::Write, 83 | { 84 | // Send address. 85 | block!(serial_tx.write(SLAVE_ADDR as u16 | 0x100)).unwrap(); 86 | 87 | // Send message len. 88 | assert!(msg.len() <= MSG_MAX_LEN); 89 | block!(serial_tx.write(msg.len() as u8)).unwrap(); 90 | 91 | // Send message. 92 | for &b in msg { 93 | block!(serial_tx.write(b)).unwrap(); 94 | } 95 | } 96 | 97 | #[entry] 98 | fn main() -> ! { 99 | // Get access to the device specific peripherals from the peripheral access crate. 100 | let p = pac::Peripherals::take().unwrap(); 101 | 102 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 103 | // HAL structs. 104 | let mut flash = p.FLASH.constrain(); 105 | let rcc = p.RCC.constrain(); 106 | 107 | // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 108 | // `clocks`. 109 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 110 | 111 | // Prepare the alternate function I/O registers. 112 | //let mut afio = p.AFIO.constrain(); 113 | 114 | // Prepare the GPIOB peripheral. 115 | let gpiob = p.GPIOB.split(); 116 | 117 | let tx_pin = gpiob.pb10; 118 | let rx_pin = gpiob.pb11; 119 | 120 | // Set up the usart device. Take ownership over the USART register and tx/rx pins. The rest of 121 | // the registers are used to enable and configure the device. 122 | // 123 | //let serial = Serial::<_, PushPull, _>::new(p.USART3, 124 | // or shorter 125 | let serial = p.USART3.serial::( 126 | (tx_pin, rx_pin), 127 | Config::default() 128 | .baudrate(9600.bps()) 129 | .wordlength_9bits() 130 | .parity_none(), 131 | &clocks, 132 | ); 133 | 134 | // Split the serial struct into a transmitting and a receiving part. 135 | let (mut serial_tx, mut serial_rx) = serial.split(); 136 | 137 | let mut buf = [0u8; MSG_MAX_LEN]; 138 | 139 | // loopback 140 | loop { 141 | // Receive message from master device. 142 | let received_msg_len = receive_msg(&mut serial_rx, &mut buf); 143 | // Send the received message back. 144 | send_msg(&mut serial_tx, &buf[..received_msg_len]); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /examples/serial_config.rs: -------------------------------------------------------------------------------- 1 | //! Serial Config test 2 | 3 | #![deny(unsafe_code)] 4 | #![allow(clippy::empty_loop)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | use panic_halt as _; 9 | 10 | use nb::block; 11 | 12 | use cortex_m_rt::entry; 13 | use stm32f1xx_hal::{ 14 | pac, 15 | prelude::*, 16 | serial::{self, Serial}, 17 | }; 18 | 19 | #[entry] 20 | fn main() -> ! { 21 | // Get access to the device specific peripherals from the peripheral access crate 22 | let p = pac::Peripherals::take().unwrap(); 23 | 24 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 25 | // HAL structs 26 | let mut flash = p.FLASH.constrain(); 27 | let rcc = p.RCC.constrain(); 28 | 29 | // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 30 | // `clocks` 31 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 32 | 33 | // Prepare the alternate function I/O registers 34 | //let mut afio = p.AFIO.constrain(); 35 | 36 | // Prepare the GPIOB peripheral 37 | let mut gpiob = p.GPIOB.split(); 38 | 39 | // USART1 40 | // let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh); 41 | 42 | // USART1 43 | // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); 44 | 45 | // USART2 46 | // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); 47 | 48 | // USART3 49 | // Configure pb10 as a push_pull output, this will be the tx pin 50 | let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); 51 | 52 | // Set up the usart device. Take ownership over the USART register and tx pin. The rest of 53 | // the registers are used to enable and configure the device. 54 | let mut tx = Serial::tx( 55 | p.USART3, 56 | tx, 57 | serial::Config::default() 58 | .baudrate(9600.bps()) 59 | .stopbits(serial::StopBits::STOP2) 60 | .wordlength_9bits() 61 | .parity_odd(), 62 | &clocks, 63 | ); 64 | 65 | let sent = b'U'; 66 | block!(tx.write_u8(sent)).unwrap(); 67 | block!(tx.write_u8(sent)).unwrap(); 68 | 69 | loop {} 70 | } 71 | -------------------------------------------------------------------------------- /examples/serial_reconfigure.rs: -------------------------------------------------------------------------------- 1 | //! Serial interface reconfiguration test 2 | //! 3 | //! You have to short the TX and RX pins to make this program work 4 | 5 | #![allow(clippy::empty_loop)] 6 | #![deny(unsafe_code)] 7 | #![no_main] 8 | #![no_std] 9 | 10 | use panic_halt as _; 11 | 12 | use cortex_m::asm; 13 | 14 | use nb::block; 15 | 16 | use cortex_m_rt::entry; 17 | use stm32f1xx_hal::{ 18 | pac, 19 | prelude::*, 20 | serial::{self, Config, Serial}, 21 | }; 22 | 23 | #[entry] 24 | fn main() -> ! { 25 | // Get access to the device specific peripherals from the peripheral access crate 26 | let p = pac::Peripherals::take().unwrap(); 27 | 28 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 29 | // HAL structs 30 | let mut flash = p.FLASH.constrain(); 31 | let rcc = p.RCC.constrain(); 32 | 33 | // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 34 | // `clocks` 35 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 36 | 37 | // Prepare the alternate function I/O registers 38 | //let mut afio = p.AFIO.constrain(); 39 | 40 | // Prepare the GPIOB peripheral 41 | let mut gpiob = p.GPIOB.split(); 42 | 43 | // USART1 44 | // let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh); 45 | // let rx = gpioa.pa10; 46 | 47 | // USART1 48 | // let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); 49 | // let rx = gpiob.pb7; 50 | 51 | // USART2 52 | // let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); 53 | // let rx = gpioa.pa3; 54 | 55 | // USART3 56 | // Configure pb10 as a push_pull output, this will be the tx pin 57 | let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); 58 | // Take ownership over pb11 59 | let rx = gpiob.pb11; 60 | 61 | // Set up the usart device. Take ownership over the USART register and tx/rx pins. The rest of 62 | // the registers are used to enable and configure the device. 63 | let mut serial = Serial::new( 64 | p.USART3, 65 | (tx, rx), 66 | Config::default().baudrate(9600.bps()), 67 | &clocks, 68 | ); 69 | 70 | // Loopback test. Write `X` and wait until the write is successful. 71 | let sent = b'X'; 72 | block!(serial.tx.write_u8(sent)).unwrap(); 73 | 74 | // Read the byte that was just sent. Blocks until the read is complete 75 | let received = block!(serial.rx.read()).unwrap(); 76 | 77 | // Since we have connected tx and rx, the byte we sent should be the one we received 78 | assert_eq!(received, sent); 79 | 80 | // Trigger a breakpoint to allow us to inspect the values 81 | asm::bkpt(); 82 | 83 | // You can reconfigure the serial port to use a different baud rate at runtime. 84 | // This may block for a while if the transmission is still in progress. 85 | block!(serial.reconfigure(Config::default().baudrate(115_200.bps()), &clocks)).unwrap(); 86 | 87 | // Let's see if it works.' 88 | let sent = b'Y'; 89 | block!(serial.tx.write_u8(sent)).unwrap(); 90 | let received = block!(serial.rx.read()).unwrap(); 91 | assert_eq!(received, sent); 92 | asm::bkpt(); 93 | 94 | // You can reconfigure the serial port after split. 95 | let (mut tx, mut rx) = serial.split(); 96 | block!(serial::reconfigure( 97 | &mut tx, 98 | &mut rx, 99 | Config::default().baudrate(9600.bps()), 100 | &clocks 101 | )) 102 | .unwrap(); 103 | 104 | loop {} 105 | } 106 | -------------------------------------------------------------------------------- /examples/spi-dma.rs: -------------------------------------------------------------------------------- 1 | //! Transmits data over an SPI port using DMA 2 | 3 | #![allow(clippy::empty_loop)] 4 | #![no_std] 5 | #![no_main] 6 | 7 | use panic_halt as _; 8 | 9 | use cortex_m_rt::entry; 10 | use stm32f1xx_hal::{ 11 | pac, 12 | prelude::*, 13 | spi::{Mode, Phase, Polarity, Spi}, 14 | }; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | // Get access to the device specific peripherals from the peripheral access crate 19 | let dp = pac::Peripherals::take().unwrap(); 20 | 21 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 22 | // HAL structs 23 | let mut flash = dp.FLASH.constrain(); 24 | let rcc = dp.RCC.constrain(); 25 | 26 | // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 27 | // `clocks` 28 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 29 | 30 | // Acquire the GPIOB peripheral 31 | let gpiob = dp.GPIOB.split(); 32 | 33 | let pins = (Some(gpiob.pb13), Some(gpiob.pb14), Some(gpiob.pb15)); 34 | 35 | let spi_mode = Mode { 36 | polarity: Polarity::IdleLow, 37 | phase: Phase::CaptureOnFirstTransition, 38 | }; 39 | let spi = Spi::new(dp.SPI2, pins, spi_mode, 100.kHz(), &clocks); 40 | 41 | // Set up the DMA device 42 | let dma = dp.DMA1.split(); 43 | 44 | // Connect the SPI device to the DMA 45 | let spi_dma = spi.with_tx_dma(dma.5); 46 | 47 | // Start a DMA transfer 48 | let transfer = spi_dma.write(b"hello, world"); 49 | 50 | // Wait for it to finnish. The transfer takes ownership over the SPI device 51 | // and the data being sent anb those things are returned by transfer.wait 52 | let (_buffer, _spi_dma) = transfer.wait(); 53 | 54 | loop {} 55 | } 56 | -------------------------------------------------------------------------------- /examples/spi-slave.rs: -------------------------------------------------------------------------------- 1 | //! SPI slave mode test 2 | //! 3 | //! spi1 master <-> spi2 slave 4 | //! PA5 <-SCK-> PB13 5 | //! PA6 <-MISO-> PB14 6 | //! PA7 <-MOSI-> PB15 7 | 8 | #![allow(clippy::empty_loop)] 9 | #![no_std] 10 | #![no_main] 11 | 12 | use cortex_m_rt::entry; 13 | use panic_halt as _; 14 | 15 | use cortex_m::{asm, singleton}; 16 | use stm32f1xx_hal::spi::{Mode, Phase, Polarity}; 17 | pub const MODE: Mode = Mode { 18 | phase: Phase::CaptureOnSecondTransition, 19 | polarity: Polarity::IdleHigh, 20 | }; 21 | 22 | use stm32f1xx_hal::{ 23 | pac::{self, interrupt, Peripherals, SPI2}, 24 | prelude::*, 25 | spi::{Event, SpiSlave}, 26 | }; 27 | 28 | static mut SPI2SLAVE: Option> = None; 29 | 30 | #[entry] 31 | fn main() -> ! { 32 | let dp = Peripherals::take().unwrap(); 33 | 34 | let mut flash = dp.FLASH.constrain(); 35 | let rcc = dp.RCC.constrain(); 36 | 37 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 38 | 39 | let gpioa = dp.GPIOA.split(); 40 | let gpiob = dp.GPIOB.split(); 41 | 42 | // SPI1 43 | // Convert pins during SPI initialization 44 | let sck = gpioa.pa5; 45 | let miso = gpioa.pa6; 46 | let mosi = gpioa.pa7; 47 | 48 | let spi1 = dp 49 | .SPI1 50 | .spi((Some(sck), Some(miso), Some(mosi)), MODE, 10.kHz(), &clocks); 51 | 52 | // SPI2 53 | // Convert pins before SPI initialization 54 | let sck = gpiob.pb13; 55 | let miso = gpiob.pb14; 56 | let mosi = gpiob.pb15; 57 | 58 | let spi2 = dp.SPI2.spi_slave((Some(sck), Some(miso), Some(mosi)), MODE); 59 | 60 | // Set up the DMA device 61 | let dma = dp.DMA1.split(); 62 | 63 | let master_spi_dma = spi1.with_rx_tx_dma(dma.2, dma.3); 64 | let slave_spi_dma = spi2.with_rx_tx_dma(dma.4, dma.5); 65 | 66 | let master_buf = singleton!(: [u8; 12] = [0; 12]).unwrap(); 67 | let slave_buf = singleton!(: [u8; 12] = [0; 12]).unwrap(); 68 | 69 | // Make sure the buffers are the same length 70 | let slave_transfer = slave_spi_dma.read_write(slave_buf, b"hello,master"); 71 | let master_transfer = master_spi_dma.read_write(master_buf, b"hello, slave"); 72 | 73 | let (buffer, spi1_dma) = master_transfer.wait(); 74 | let (_buffer, spi2_dma) = slave_transfer.wait(); 75 | 76 | asm::bkpt(); 77 | 78 | // test SPI with interrupts 79 | let (mut spi2, _, _) = spi2_dma.release(); 80 | 81 | spi2.listen(Event::Rxne); 82 | spi2.listen(Event::Txe); 83 | spi2.listen(Event::Error); 84 | 85 | cortex_m::interrupt::free(|_| unsafe { 86 | SPI2SLAVE.replace(spi2); 87 | }); 88 | 89 | unsafe { 90 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::SPI2); 91 | } 92 | 93 | let master_transfer = spi1_dma.read_write(buffer.0, buffer.1); 94 | let (_buffer, _spi1_dma) = master_transfer.wait(); 95 | 96 | loop {} 97 | } 98 | 99 | const R_BUFFER_LEN: usize = 16; 100 | static mut R_BUFFER: &mut [u8; R_BUFFER_LEN] = &mut [0; R_BUFFER_LEN]; 101 | static mut RIDX: usize = 0; 102 | 103 | const W_BUFFER_LEN: usize = 3; 104 | static W_BUFFER: &[u8; W_BUFFER_LEN] = &[1, 2, 3]; 105 | static mut WIDX: usize = 0; 106 | 107 | #[interrupt] 108 | unsafe fn SPI2() { 109 | cortex_m::interrupt::free(|_| { 110 | if let Some(spi2) = SPI2SLAVE.as_mut() { 111 | if spi2.is_overrun() { 112 | // mcu processing speed is not enough 113 | asm::bkpt(); 114 | } 115 | if spi2.is_rx_not_empty() { 116 | if let Ok(w) = nb::block!(spi2.read_nonblocking()) { 117 | R_BUFFER[RIDX] = w; 118 | RIDX += 1; 119 | if RIDX >= R_BUFFER_LEN - 1 { 120 | RIDX = 0; 121 | } 122 | } 123 | } 124 | if spi2.is_tx_empty() { 125 | if let Ok(()) = nb::block!(spi2.write_nonblocking(W_BUFFER[WIDX])) { 126 | WIDX += 1; 127 | if WIDX >= W_BUFFER_LEN { 128 | WIDX = 0; 129 | } 130 | } 131 | } 132 | } 133 | }) 134 | } 135 | -------------------------------------------------------------------------------- /examples/spi.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![allow(clippy::empty_loop)] 3 | #![no_std] 4 | #![no_main] 5 | 6 | use cortex_m_rt::entry; 7 | use panic_halt as _; 8 | 9 | pub const MODE: Mode = Mode { 10 | phase: Phase::CaptureOnSecondTransition, 11 | polarity: Polarity::IdleHigh, 12 | }; 13 | 14 | use stm32f1xx_hal::{ 15 | gpio::{Output, PA4}, 16 | pac::{Peripherals, SPI1}, 17 | prelude::*, 18 | spi::{Mode, Phase, Polarity, Spi}, 19 | }; 20 | 21 | fn setup() -> (Spi, PA4) { 22 | let dp = Peripherals::take().unwrap(); 23 | 24 | let mut flash = dp.FLASH.constrain(); 25 | let rcc = dp.RCC.constrain(); 26 | 27 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 28 | 29 | //let mut afio = dp.AFIO.constrain(); 30 | let mut gpioa = dp.GPIOA.split(); 31 | 32 | // SPI1 33 | let sck = gpioa.pa5; 34 | let miso = gpioa.pa6; 35 | let mosi = gpioa.pa7; 36 | let cs = gpioa.pa4.into_push_pull_output(&mut gpioa.crl); 37 | 38 | let spi = dp 39 | .SPI1 40 | //.remap(&mut afio.mapr) // if you want to use PB3, PB4, PB5 41 | .spi((Some(sck), Some(miso), Some(mosi)), MODE, 1.MHz(), &clocks); 42 | 43 | (spi, cs) 44 | } 45 | 46 | #[entry] 47 | fn main() -> ! { 48 | let (_spi, _cs) = setup(); 49 | 50 | loop {} 51 | } 52 | -------------------------------------------------------------------------------- /examples/timer-interrupt-rtic.rs: -------------------------------------------------------------------------------- 1 | //! Uses the timer interrupt to blink a led with different frequencies. 2 | //! 3 | //! This assumes that a LED is connected to pc13 as is the case on the blue pill board. 4 | //! 5 | //! Note: Without additional hardware, PC13 should not be used to drive an LED, see page 5.1.2 of 6 | //! the reference manual for an explanation. This is not an issue on the blue pill. 7 | 8 | #![no_std] 9 | #![no_main] 10 | 11 | // you can put a breakpoint on `rust_begin_unwind` to catch panics 12 | use panic_halt as _; 13 | 14 | #[rtic::app(device = stm32f1xx_hal::pac)] 15 | mod app { 16 | use stm32f1xx_hal::{ 17 | gpio::{gpioc::PC13, Output, PinState, PushPull}, 18 | pac, 19 | prelude::*, 20 | timer::{CounterMs, Event}, 21 | }; 22 | 23 | #[shared] 24 | struct Shared {} 25 | 26 | #[local] 27 | struct Local { 28 | led: PC13>, 29 | timer_handler: CounterMs, 30 | } 31 | 32 | #[init] 33 | fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { 34 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 35 | // HAL structs 36 | let mut flash = cx.device.FLASH.constrain(); 37 | let rcc = cx.device.RCC.constrain(); 38 | 39 | // Freeze the configuration of all the clocks in the system and store the frozen frequencies 40 | // in `clocks` 41 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 42 | 43 | // Acquire the GPIOC peripheral 44 | let mut gpioc = cx.device.GPIOC.split(); 45 | 46 | // Configure gpio C pin 13 as a push-pull output. The `crh` register is passed to the 47 | // function in order to configure the port. For pins 0-7, crl should be passed instead 48 | let led = gpioc 49 | .pc13 50 | .into_push_pull_output_with_state(&mut gpioc.crh, PinState::High); 51 | // Configure the syst timer to trigger an update every second and enables interrupt 52 | let mut timer = cx.device.TIM1.counter_ms(&clocks); 53 | timer.start(1.secs()).unwrap(); 54 | timer.listen(Event::Update); 55 | 56 | // Init the static resources to use them later through RTIC 57 | ( 58 | Shared {}, 59 | Local { 60 | led, 61 | timer_handler: timer, 62 | }, 63 | init::Monotonics(), 64 | ) 65 | } 66 | 67 | // Optional. 68 | // 69 | // https://rtic.rs/dev/book/en/by-example/app_idle.html 70 | // > When no idle function is declared, the runtime sets the SLEEPONEXIT bit and then 71 | // > sends the microcontroller to sleep after running init. 72 | #[idle] 73 | fn idle(_cx: idle::Context) -> ! { 74 | loop { 75 | cortex_m::asm::wfi(); 76 | } 77 | } 78 | 79 | #[task(binds = TIM1_UP, priority = 1, local = [led, timer_handler, led_state: bool = false, count: u8 = 0])] 80 | fn tick(cx: tick::Context) { 81 | // Depending on the application, you could want to delegate some of the work done here to 82 | // the idle task if you want to minimize the latency of interrupts with same priority (if 83 | // you have any). That could be done 84 | if *cx.local.led_state { 85 | // Uses resources managed by rtic to turn led off (on bluepill) 86 | cx.local.led.set_high(); 87 | *cx.local.led_state = false; 88 | } else { 89 | cx.local.led.set_low(); 90 | *cx.local.led_state = true; 91 | } 92 | // Count used to change the timer update frequency 93 | *cx.local.count += 1; 94 | 95 | if *cx.local.count == 4 { 96 | // Changes timer update frequency 97 | cx.local.timer_handler.start(500.millis()).unwrap(); 98 | } else if *cx.local.count == 12 { 99 | cx.local.timer_handler.start(1.secs()).unwrap(); 100 | *cx.local.count = 0; 101 | } 102 | 103 | // Clears the update flag 104 | cx.local.timer_handler.clear_interrupt(Event::Update); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /examples/usb_serial.rs: -------------------------------------------------------------------------------- 1 | //! CDC-ACM serial port example using polling in a busy loop. 2 | //! Target board: Blue Pill 3 | //! 4 | //! Note: 5 | //! When building this since this is a larger program, 6 | //! one would need to build it using release profile 7 | //! since debug profiles generates artifacts that 8 | //! cause FLASH overflow errors due to their size 9 | #![no_std] 10 | #![no_main] 11 | 12 | extern crate panic_semihosting; 13 | 14 | use cortex_m::asm::delay; 15 | use cortex_m_rt::entry; 16 | use stm32f1xx_hal::usb::{Peripheral, UsbBus}; 17 | use stm32f1xx_hal::{pac, prelude::*}; 18 | use usb_device::prelude::*; 19 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 20 | 21 | #[entry] 22 | fn main() -> ! { 23 | let dp = pac::Peripherals::take().unwrap(); 24 | 25 | let mut flash = dp.FLASH.constrain(); 26 | let rcc = dp.RCC.constrain(); 27 | 28 | let clocks = rcc 29 | .cfgr 30 | .use_hse(8.MHz()) 31 | .sysclk(48.MHz()) 32 | .pclk1(24.MHz()) 33 | .freeze(&mut flash.acr); 34 | 35 | assert!(clocks.usbclk_valid()); 36 | 37 | // Configure the on-board LED (PC13, green) 38 | let mut gpioc = dp.GPIOC.split(); 39 | let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 40 | led.set_high(); // Turn off 41 | 42 | let mut gpioa = dp.GPIOA.split(); 43 | 44 | // BluePill board has a pull-up resistor on the D+ line. 45 | // Pull the D+ pin down to send a RESET condition to the USB bus. 46 | // This forced reset is needed only for development, without it host 47 | // will not reset your device when you upload new firmware. 48 | let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); 49 | usb_dp.set_low(); 50 | delay(clocks.sysclk().raw() / 100); 51 | 52 | let usb = Peripheral { 53 | usb: dp.USB, 54 | pin_dm: gpioa.pa11, 55 | pin_dp: usb_dp.into_floating_input(&mut gpioa.crh), 56 | }; 57 | let usb_bus = UsbBus::new(usb); 58 | 59 | let mut serial = SerialPort::new(&usb_bus); 60 | 61 | let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) 62 | .device_class(USB_CLASS_CDC) 63 | .strings(&[StringDescriptors::default() 64 | .manufacturer("Fake Company") 65 | .product("Serial port") 66 | .serial_number("TEST")]) 67 | .unwrap() 68 | .build(); 69 | 70 | loop { 71 | if !usb_dev.poll(&mut [&mut serial]) { 72 | continue; 73 | } 74 | 75 | let mut buf = [0u8; 64]; 76 | 77 | match serial.read(&mut buf) { 78 | Ok(count) if count > 0 => { 79 | led.set_low(); // Turn on 80 | 81 | // Echo back in upper case 82 | for c in buf[0..count].iter_mut() { 83 | if 0x61 <= *c && *c <= 0x7a { 84 | *c &= !0x20; 85 | } 86 | } 87 | 88 | let mut write_offset = 0; 89 | while write_offset < count { 90 | match serial.write(&buf[write_offset..count]) { 91 | Ok(len) if len > 0 => { 92 | write_offset += len; 93 | } 94 | _ => {} 95 | } 96 | } 97 | } 98 | _ => {} 99 | } 100 | 101 | led.set_high(); // Turn off 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /examples/usb_serial_interrupt.rs: -------------------------------------------------------------------------------- 1 | //! CDC-ACM serial port example using interrupts. 2 | //! Target board: Blue Pill 3 | #![no_std] 4 | #![no_main] 5 | 6 | extern crate panic_semihosting; 7 | 8 | use cortex_m::asm::{delay, wfi}; 9 | use cortex_m_rt::entry; 10 | use stm32f1xx_hal::pac::{self, interrupt, Interrupt, NVIC}; 11 | use stm32f1xx_hal::prelude::*; 12 | use stm32f1xx_hal::usb::{Peripheral, UsbBus, UsbBusType}; 13 | use usb_device::{bus::UsbBusAllocator, prelude::*}; 14 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 15 | 16 | static mut USB_BUS: Option> = None; 17 | static mut USB_SERIAL: Option> = None; 18 | static mut USB_DEVICE: Option> = None; 19 | 20 | #[entry] 21 | fn main() -> ! { 22 | let dp = pac::Peripherals::take().unwrap(); 23 | 24 | let mut flash = dp.FLASH.constrain(); 25 | let rcc = dp.RCC.constrain(); 26 | 27 | let clocks = rcc 28 | .cfgr 29 | .use_hse(8.MHz()) 30 | .sysclk(48.MHz()) 31 | .pclk1(24.MHz()) 32 | .freeze(&mut flash.acr); 33 | 34 | assert!(clocks.usbclk_valid()); 35 | 36 | let mut gpioa = dp.GPIOA.split(); 37 | 38 | // BluePill board has a pull-up resistor on the D+ line. 39 | // Pull the D+ pin down to send a RESET condition to the USB bus. 40 | // This forced reset is needed only for development, without it host 41 | // will not reset your device when you upload new firmware. 42 | let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); 43 | usb_dp.set_low(); 44 | delay(clocks.sysclk().raw() / 100); 45 | 46 | let usb_dm = gpioa.pa11; 47 | let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh); 48 | 49 | let usb = Peripheral { 50 | usb: dp.USB, 51 | pin_dm: usb_dm, 52 | pin_dp: usb_dp, 53 | }; 54 | 55 | // Unsafe to allow access to static variables 56 | unsafe { 57 | let bus = UsbBus::new(usb); 58 | 59 | USB_BUS = Some(bus); 60 | 61 | USB_SERIAL = Some(SerialPort::new(USB_BUS.as_ref().unwrap())); 62 | 63 | let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27dd)) 64 | .device_class(USB_CLASS_CDC) 65 | .strings(&[StringDescriptors::default() 66 | .manufacturer("Fake Company") 67 | .product("Serial port") 68 | .serial_number("TEST")]) 69 | .unwrap() 70 | .build(); 71 | 72 | USB_DEVICE = Some(usb_dev); 73 | } 74 | 75 | unsafe { 76 | NVIC::unmask(Interrupt::USB_HP_CAN_TX); 77 | NVIC::unmask(Interrupt::USB_LP_CAN_RX0); 78 | } 79 | 80 | loop { 81 | wfi(); 82 | } 83 | } 84 | 85 | #[interrupt] 86 | fn USB_HP_CAN_TX() { 87 | usb_interrupt(); 88 | } 89 | 90 | #[interrupt] 91 | fn USB_LP_CAN_RX0() { 92 | usb_interrupt(); 93 | } 94 | 95 | fn usb_interrupt() { 96 | let usb_dev = unsafe { USB_DEVICE.as_mut().unwrap() }; 97 | let serial = unsafe { USB_SERIAL.as_mut().unwrap() }; 98 | 99 | if !usb_dev.poll(&mut [serial]) { 100 | return; 101 | } 102 | 103 | let mut buf = [0u8; 8]; 104 | 105 | match serial.read(&mut buf) { 106 | Ok(count) if count > 0 => { 107 | // Echo back in upper case 108 | for c in buf[0..count].iter_mut() { 109 | if 0x61 <= *c && *c <= 0x7a { 110 | *c &= !0x20; 111 | } 112 | } 113 | 114 | serial.write(&buf[0..count]).ok(); 115 | } 116 | _ => {} 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /examples/usb_serial_rtic.rs: -------------------------------------------------------------------------------- 1 | //! CDC-ACM serial port example using cortex-m-rtic. 2 | //! Target board: Blue Pill 3 | #![no_main] 4 | #![no_std] 5 | #![allow(non_snake_case)] 6 | #![deny(unsafe_code)] 7 | 8 | use panic_semihosting as _; 9 | 10 | #[rtic::app(device = stm32f1xx_hal::pac)] 11 | mod app { 12 | use cortex_m::asm::delay; 13 | use stm32f1xx_hal::prelude::*; 14 | use stm32f1xx_hal::usb::{Peripheral, UsbBus, UsbBusType}; 15 | use usb_device::prelude::*; 16 | 17 | #[shared] 18 | struct Shared { 19 | usb_dev: UsbDevice<'static, UsbBusType>, 20 | serial: usbd_serial::SerialPort<'static, UsbBusType>, 21 | } 22 | 23 | #[local] 24 | struct Local {} 25 | 26 | #[init(local = [usb_bus: Option> = None])] 27 | fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { 28 | let mut flash = cx.device.FLASH.constrain(); 29 | let rcc = cx.device.RCC.constrain(); 30 | 31 | let clocks = rcc 32 | .cfgr 33 | .use_hse(8.MHz()) 34 | .sysclk(48.MHz()) 35 | .pclk1(24.MHz()) 36 | .freeze(&mut flash.acr); 37 | 38 | assert!(clocks.usbclk_valid()); 39 | 40 | let mut gpioa = cx.device.GPIOA.split(); 41 | 42 | // BluePill board has a pull-up resistor on the D+ line. 43 | // Pull the D+ pin down to send a RESET condition to the USB bus. 44 | // This forced reset is needed only for development, without it host 45 | // will not reset your device when you upload new firmware. 46 | let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); 47 | usb_dp.set_low(); 48 | delay(clocks.sysclk().raw() / 100); 49 | 50 | let usb_dm = gpioa.pa11; 51 | let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh); 52 | 53 | let usb = Peripheral { 54 | usb: cx.device.USB, 55 | pin_dm: usb_dm, 56 | pin_dp: usb_dp, 57 | }; 58 | 59 | cx.local.usb_bus.replace(UsbBus::new(usb)); 60 | let usb_bus = cx.local.usb_bus.as_ref().unwrap(); 61 | 62 | let serial = usbd_serial::SerialPort::new(usb_bus); 63 | let usb_dev = UsbDeviceBuilder::new(usb_bus, UsbVidPid(0x16c0, 0x27dd)) 64 | .device_class(usbd_serial::USB_CLASS_CDC) 65 | .strings(&[StringDescriptors::default() 66 | .manufacturer("Fake Company") 67 | .product("Serial port") 68 | .serial_number("TEST")]) 69 | .unwrap() 70 | .build(); 71 | 72 | (Shared { usb_dev, serial }, Local {}, init::Monotonics()) 73 | } 74 | 75 | #[task(binds = USB_HP_CAN_TX, shared = [usb_dev, serial])] 76 | fn usb_tx(cx: usb_tx::Context) { 77 | let mut usb_dev = cx.shared.usb_dev; 78 | let mut serial = cx.shared.serial; 79 | 80 | (&mut usb_dev, &mut serial).lock(|usb_dev, serial| { 81 | super::usb_poll(usb_dev, serial); 82 | }); 83 | } 84 | 85 | #[task(binds = USB_LP_CAN_RX0, shared = [usb_dev, serial])] 86 | fn usb_rx0(cx: usb_rx0::Context) { 87 | let mut usb_dev = cx.shared.usb_dev; 88 | let mut serial = cx.shared.serial; 89 | 90 | (&mut usb_dev, &mut serial).lock(|usb_dev, serial| { 91 | super::usb_poll(usb_dev, serial); 92 | }); 93 | } 94 | } 95 | 96 | fn usb_poll( 97 | usb_dev: &mut usb_device::prelude::UsbDevice<'static, B>, 98 | serial: &mut usbd_serial::SerialPort<'static, B>, 99 | ) { 100 | if !usb_dev.poll(&mut [serial]) { 101 | return; 102 | } 103 | 104 | let mut buf = [0u8; 8]; 105 | 106 | match serial.read(&mut buf) { 107 | Ok(count) if count > 0 => { 108 | // Echo back in upper case 109 | for c in buf[0..count].iter_mut() { 110 | if 0x61 <= *c && *c <= 0x7a { 111 | *c &= !0x20; 112 | } 113 | } 114 | 115 | serial.write(&buf[0..count]).ok(); 116 | } 117 | _ => {} 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | /* Linker script for the STM32F103C8T6 */ 2 | MEMORY 3 | { 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 64K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 20K 6 | } 7 | -------------------------------------------------------------------------------- /src/backup_domain.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Registers that are not reset as long as Vbat or Vdd has power. 3 | 4 | The registers retain their values during wakes from standby mode or system resets. They also 5 | retain their value when Vdd is switched off as long as V_BAT is powered. 6 | 7 | The backup domain also contains tamper protection and writes to it must be enabled in order 8 | to use the real time clock (RTC). 9 | 10 | Write access to the backup domain is enabled in RCC using the `rcc::Rcc::BKP::constrain()` 11 | function. 12 | 13 | Only the RTC functionality is currently implemented. 14 | */ 15 | 16 | use crate::pac::BKP; 17 | 18 | /** 19 | The existence of this struct indicates that writing to the the backup 20 | domain has been enabled. It is acquired by calling `constrain` on `rcc::Rcc::BKP` 21 | */ 22 | pub struct BackupDomain { 23 | pub(crate) _regs: BKP, 24 | } 25 | 26 | macro_rules! write_drx { 27 | ($self:ident, $drx:ident, $idx:expr, $new:expr) => { 28 | $self._regs.$drx($idx).write(|w| w.d().set($new)) 29 | }; 30 | } 31 | 32 | macro_rules! read_drx { 33 | ($self:ident, $drx:ident, $idx:expr) => { 34 | $self._regs.$drx($idx).read().d().bits() 35 | }; 36 | } 37 | 38 | impl BackupDomain { 39 | /// Read a 16-bit value from one of the DR1 to DR10 registers part of the 40 | /// Backup Data Register. The register argument is a zero based index to the 41 | /// DRx registers: 0 is DR1, up to 9 for DR10. Providing a number above 9 42 | /// will panic. 43 | pub fn read_data_register_low(&self, register: usize) -> u16 { 44 | read_drx!(self, dr, register) 45 | } 46 | 47 | /// Read a 16-bit value from one of the DR11 to DR42 registers part of the 48 | /// Backup Data Register. The register argument is a zero based index to the 49 | /// DRx registers: 0 is DR11, up to 31 for DR42. Providing a number above 31 50 | /// will panic. 51 | /// NOTE: not available on medium- and low-density devices! 52 | #[cfg(any(feature = "high", feature = "connectivity"))] 53 | pub fn read_data_register_high(&self, register: usize) -> u16 { 54 | read_drx!(self, bkp_dr, register) 55 | } 56 | 57 | /// Write a 16-bit value to one of the DR1 to DR10 registers part of the 58 | /// Backup Data Register. The register argument is a zero based index to the 59 | /// DRx registers: 0 is DR1, up to 9 for DR10. Providing a number above 9 60 | /// will panic. 61 | pub fn write_data_register_low(&self, register: usize, data: u16) { 62 | write_drx!(self, dr, register, data); 63 | } 64 | 65 | /// Write a 16-bit value to one of the DR11 to DR42 registers part of the 66 | /// Backup Data Register. The register argument is a zero based index to the 67 | /// DRx registers: 0 is DR11, up to 31 for DR42. Providing a number above 31 68 | /// will panic. 69 | /// NOTE: not available on medium- and low-density devices! 70 | #[cfg(any(feature = "high", feature = "connectivity"))] 71 | pub fn write_data_register_high(&self, register: usize, data: u16) { 72 | write_drx!(self, bkp_dr, register, data); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/bb.rs: -------------------------------------------------------------------------------- 1 | //! Bit banding 2 | //! 3 | //! Support for the manipulation of peripheral registers through bit-banding. 4 | //! Not all peripherals are mapped to the bit-banding alias region, the peripheral bit-band region 5 | //! is from `0x4000_0000` to `0x400F_FFFF`. Bit-banding allows the manipulation of individual bits 6 | //! atomically. 7 | 8 | use core::ptr; 9 | 10 | // Start address of the peripheral memory region capable of being addressed by bit-banding 11 | const PERI_ADDRESS_START: usize = 0x4000_0000; 12 | const PERI_ADDRESS_END: usize = 0x400F_FFFF; 13 | 14 | const PERI_BIT_BAND_BASE: usize = 0x4200_0000; 15 | 16 | /// Clears the bit on the provided register without modifying other bits. 17 | /// 18 | /// # Safety 19 | /// 20 | /// Some registers have reserved bits which should not be modified. 21 | #[inline] 22 | pub unsafe fn clear(register: *const T, bit: u8) { 23 | write(register, bit, false); 24 | } 25 | 26 | /// Sets the bit on the provided register without modifying other bits. 27 | /// 28 | /// # Safety 29 | /// 30 | /// Some registers have reserved bits which should not be modified. 31 | #[inline] 32 | pub unsafe fn set(register: *const T, bit: u8) { 33 | write(register, bit, true); 34 | } 35 | 36 | /// Sets or clears the bit on the provided register without modifying other bits. 37 | /// 38 | /// # Safety 39 | /// 40 | /// Some registers have reserved bits which should not be modified. 41 | #[inline] 42 | pub unsafe fn write(register: *const T, bit: u8, set: bool) { 43 | let addr = register as usize; 44 | 45 | assert!((PERI_ADDRESS_START..=PERI_ADDRESS_END).contains(&addr)); 46 | assert!(bit < 32); 47 | 48 | let bit = bit as usize; 49 | let bb_addr = (PERI_BIT_BAND_BASE + (addr - PERI_ADDRESS_START) * 32) + 4 * bit; 50 | ptr::write_volatile(bb_addr as *mut u32, u32::from(set)); 51 | } 52 | -------------------------------------------------------------------------------- /src/can.rs: -------------------------------------------------------------------------------- 1 | //! # Controller Area Network (CAN) Interface 2 | //! 3 | //! ## Alternate function remapping 4 | //! 5 | //! ### CAN1 6 | //! 7 | //! | Function \ Remap | 0 (default) | 1 | 8 | //! |------------------|-------------|------| 9 | //! | TX (A-PP) | PA12 | PB9 | 10 | //! | RX (I-F/PU) | PA11 | PB8 | 11 | //! 12 | //! ### CAN2 13 | //! 14 | //! | Function \ Remap | 0 (default) | 1 | 15 | //! |------------------|-------------|------| 16 | //! | TX (A-PP) | PB6 | PB13 | 17 | //! | RX (I-F/PU) | PB5 | PB12 | 18 | 19 | use crate::afio::{self, RInto, Rmp}; 20 | use crate::gpio::{Floating, UpMode}; 21 | use crate::pac::{self, RCC}; 22 | 23 | pub trait CanExt: Sized + Instance { 24 | fn can( 25 | self, 26 | #[cfg(not(feature = "connectivity"))] usb: pac::USB, 27 | pins: (impl RInto, impl RInto, 0>), 28 | ) -> Can; 29 | fn can_loopback( 30 | self, 31 | #[cfg(not(feature = "connectivity"))] usb: pac::USB, 32 | ) -> Can; 33 | } 34 | 35 | impl CanExt for CAN { 36 | fn can( 37 | self, 38 | #[cfg(not(feature = "connectivity"))] usb: pac::USB, 39 | pins: (impl RInto, impl RInto, 0>), 40 | ) -> Can { 41 | Can::new( 42 | self, 43 | #[cfg(not(feature = "connectivity"))] 44 | usb, 45 | pins, 46 | ) 47 | } 48 | fn can_loopback( 49 | self, 50 | #[cfg(not(feature = "connectivity"))] usb: pac::USB, 51 | ) -> Can { 52 | Can::new_loopback( 53 | self, 54 | #[cfg(not(feature = "connectivity"))] 55 | usb, 56 | ) 57 | } 58 | } 59 | 60 | pub trait Instance: crate::rcc::Enable + afio::CanCommon {} 61 | #[cfg(not(feature = "connectivity"))] 62 | use pac::CAN as CAN1; 63 | #[cfg(feature = "connectivity")] 64 | use pac::CAN1; 65 | 66 | impl Instance for CAN1 {} 67 | #[cfg(feature = "connectivity")] 68 | impl Instance for pac::CAN2 {} 69 | 70 | /// Interface to the CAN peripheral. 71 | #[allow(unused)] 72 | pub struct Can { 73 | can: CAN, 74 | pins: (Option, Option>), 75 | } 76 | 77 | impl Rmp { 78 | pub fn can( 79 | self, 80 | #[cfg(not(feature = "connectivity"))] _usb: pac::USB, 81 | pins: (impl RInto, impl RInto, R>), 82 | ) -> Can { 83 | let rcc = unsafe { &(*RCC::ptr()) }; 84 | CAN::enable(rcc); 85 | 86 | let pins = (Some(pins.0.rinto()), Some(pins.1.rinto())); 87 | Can { can: self.0, pins } 88 | } 89 | pub fn can_loopback( 90 | self, 91 | #[cfg(not(feature = "connectivity"))] usb: pac::USB, 92 | ) -> Can { 93 | Can::new_loopback( 94 | self.0, 95 | #[cfg(not(feature = "connectivity"))] 96 | usb, 97 | ) 98 | } 99 | } 100 | 101 | impl Can { 102 | /// Creates a CAN interface. 103 | /// 104 | /// CAN shares SRAM with the USB peripheral. Take ownership of USB to 105 | /// prevent accidental shared usage. 106 | pub fn new( 107 | can: impl Into>, 108 | #[cfg(not(feature = "connectivity"))] _usb: pac::USB, 109 | pins: (impl RInto, impl RInto, R>), 110 | ) -> Can { 111 | can.into().can( 112 | #[cfg(not(feature = "connectivity"))] 113 | _usb, 114 | pins, 115 | ) 116 | } 117 | 118 | /// Creates a CAN interface in loopback mode 119 | pub fn new_loopback( 120 | can: CAN, 121 | #[cfg(not(feature = "connectivity"))] _usb: pac::USB, 122 | ) -> Can { 123 | let rcc = unsafe { &(*RCC::ptr()) }; 124 | CAN::enable(rcc); 125 | 126 | Can { 127 | can, 128 | pins: (None, None), 129 | } 130 | } 131 | } 132 | 133 | unsafe impl bxcan::Instance for Can { 134 | const REGISTERS: *mut bxcan::RegisterBlock = CAN1::ptr() as *mut _; 135 | } 136 | 137 | #[cfg(feature = "connectivity")] 138 | unsafe impl bxcan::Instance for Can { 139 | const REGISTERS: *mut bxcan::RegisterBlock = pac::CAN2::ptr() as *mut _; 140 | } 141 | 142 | unsafe impl bxcan::FilterOwner for Can { 143 | const NUM_FILTER_BANKS: u8 = 28; 144 | } 145 | 146 | #[cfg(feature = "connectivity")] 147 | unsafe impl bxcan::MasterInstance for Can {} 148 | -------------------------------------------------------------------------------- /src/crc.rs: -------------------------------------------------------------------------------- 1 | //! CRC 2 | 3 | use crate::pac::{CRC, RCC}; 4 | use crate::rcc::Enable; 5 | 6 | /// Extension trait to constrain the CRC peripheral 7 | pub trait CrcExt { 8 | /// Constrains the CRC peripheral to play nicely with the other abstractions 9 | #[allow(clippy::wrong_self_convention, clippy::new_ret_no_self)] 10 | fn new(self) -> Crc; 11 | } 12 | 13 | impl CrcExt for CRC { 14 | fn new(self) -> Crc { 15 | let rcc = unsafe { &(*RCC::ptr()) }; 16 | CRC::enable(rcc); 17 | 18 | Crc { crc: self } 19 | } 20 | } 21 | 22 | /// Constrained CRC peripheral 23 | pub struct Crc { 24 | crc: CRC, 25 | } 26 | 27 | impl Crc { 28 | pub fn read(&self) -> u32 { 29 | self.crc.dr().read().bits() 30 | } 31 | 32 | pub fn write(&mut self, val: u32) { 33 | self.crc.dr().write(|w| w.dr().set(val)); 34 | } 35 | 36 | pub fn reset(&self) { 37 | self.crc.cr().write(|w| w.reset().set_bit()); 38 | // calling CRC::dr::write() just after CRC::cr::reset() will not work as expected, and 39 | // inserting single nop() seems to solve the problem. 40 | cortex_m::asm::nop(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /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 | #![deny(unused_imports)] 6 | 7 | use crate::{ 8 | gpio::{Analog, PA4, PA5}, 9 | pac::{DAC, RCC}, 10 | rcc::{Enable, Reset}, 11 | }; 12 | 13 | pub struct C1; 14 | pub struct C2; 15 | 16 | pub trait DacOut { 17 | fn set_value(&mut self, val: V); 18 | fn get_value(&mut self) -> V; 19 | } 20 | 21 | pub trait DacPin { 22 | fn enable(&mut self); 23 | } 24 | 25 | pub trait Pins { 26 | type Output; 27 | #[doc(hidden)] 28 | fn init() -> Self::Output; 29 | } 30 | 31 | impl Pins for PA4 { 32 | type Output = C1; 33 | fn init() -> Self::Output { 34 | C1 35 | } 36 | } 37 | 38 | impl Pins for PA5 { 39 | type Output = C2; 40 | fn init() -> Self::Output { 41 | C2 42 | } 43 | } 44 | 45 | impl Pins for (PA4, PA5) { 46 | type Output = (C1, C2); 47 | fn init() -> Self::Output { 48 | (C1, C2) 49 | } 50 | } 51 | 52 | pub fn dac(_dac: DAC, _pins: PINS) -> PINS::Output 53 | where 54 | PINS: Pins, 55 | { 56 | // Enable and reset clock. 57 | let rcc = unsafe { &(*RCC::ptr()) }; 58 | DAC::enable(rcc); 59 | DAC::reset(rcc); 60 | 61 | PINS::init() 62 | } 63 | 64 | macro_rules! dac { 65 | ($CX:ident, $en:ident, $cen:ident, $cal_flag:ident, $trim:ident, $mode:ident, $dhrx:ident, $dac_dor:ident, $daccxdhr:ident) => { 66 | impl DacPin for $CX { 67 | fn enable(&mut self) { 68 | let dac = unsafe { &(*DAC::ptr()) }; 69 | dac.cr().modify(|_, w| w.$en().set_bit()); 70 | } 71 | } 72 | 73 | impl DacOut for $CX { 74 | fn set_value(&mut self, val: u16) { 75 | let dac = unsafe { &(*DAC::ptr()) }; 76 | dac.$dhrx().write(|w| unsafe { w.bits(val as u32) }); 77 | } 78 | 79 | fn get_value(&mut self) -> u16 { 80 | let dac = unsafe { &(*DAC::ptr()) }; 81 | dac.$dac_dor().read().bits() as u16 82 | } 83 | } 84 | }; 85 | } 86 | 87 | pub trait DacExt { 88 | fn constrain(self, pins: PINS) -> PINS::Output 89 | where 90 | PINS: Pins; 91 | } 92 | 93 | impl DacExt for DAC { 94 | fn constrain(self, pins: PINS) -> PINS::Output 95 | where 96 | PINS: Pins, 97 | { 98 | dac(self, pins) 99 | } 100 | } 101 | 102 | dac!(C1, en1, cen1, cal_flag1, otrim1, mode1, dhr12r1, dor1, dacc1dhr); 103 | dac!(C2, en2, cen2, cal_flag2, otrim2, mode2, dhr12r2, dor2, dacc2dhr); 104 | -------------------------------------------------------------------------------- /src/gpio/erased.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub type AnyPin = ErasedPin; 4 | 5 | macro_rules! impl_pxx { 6 | ($(($port_id:literal :: $pin:ident)),*) => { 7 | /// Erased pin 8 | /// 9 | /// `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). 10 | pub enum ErasedPin { 11 | $( 12 | $pin(PartiallyErasedPin<$port_id, MODE>) 13 | ),* 14 | } 15 | 16 | impl PinExt for ErasedPin { 17 | type Mode = MODE; 18 | 19 | #[inline(always)] 20 | fn pin_id(&self) -> u8 { 21 | match self { 22 | $(Self::$pin(pin) => pin.pin_id()),* 23 | } 24 | } 25 | 26 | #[inline(always)] 27 | fn port_id(&self) -> u8 { 28 | match self { 29 | $(Self::$pin(pin) => pin.port_id()),* 30 | } 31 | } 32 | } 33 | 34 | impl ErasedPin> { 35 | pub fn set_high(&mut self) { 36 | match self { 37 | $(Self::$pin(pin) => pin.set_high()),* 38 | } 39 | } 40 | 41 | pub fn set_low(&mut self) { 42 | match self { 43 | $(Self::$pin(pin) => pin.set_low()),* 44 | } 45 | } 46 | 47 | pub fn is_set_high(&self) -> bool { 48 | match self { 49 | $(Self::$pin(pin) => pin.is_set_high()),* 50 | } 51 | } 52 | 53 | pub fn is_set_low(&self) -> bool { 54 | match self { 55 | $(Self::$pin(pin) => pin.is_set_low()),* 56 | } 57 | } 58 | } 59 | 60 | impl ErasedPin> { 61 | pub fn is_high(&self) -> bool { 62 | match self { 63 | $(Self::$pin(pin) => pin.is_high()),* 64 | } 65 | } 66 | 67 | pub fn is_low(&self) -> bool { 68 | match self { 69 | $(Self::$pin(pin) => pin.is_low()),* 70 | } 71 | } 72 | } 73 | 74 | impl ErasedPin> { 75 | pub fn is_high(&self) -> bool { 76 | match self { 77 | $(Self::$pin(pin) => pin.is_high()),* 78 | } 79 | } 80 | 81 | pub fn is_low(&self) -> bool { 82 | match self { 83 | $(Self::$pin(pin) => pin.is_low()),* 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | impl ErasedPin> { 91 | #[inline(always)] 92 | pub fn get_state(&self) -> PinState { 93 | if self.is_set_low() { 94 | PinState::Low 95 | } else { 96 | PinState::High 97 | } 98 | } 99 | 100 | #[inline(always)] 101 | pub fn set_state(&mut self, state: PinState) { 102 | match state { 103 | PinState::Low => self.set_low(), 104 | PinState::High => self.set_high(), 105 | } 106 | } 107 | 108 | #[inline(always)] 109 | pub fn toggle(&mut self) { 110 | if self.is_set_low() { 111 | self.set_high() 112 | } else { 113 | self.set_low() 114 | } 115 | } 116 | } 117 | 118 | #[cfg(not(any(feature = "xl", feature = "high")))] 119 | impl_pxx! { 120 | ('A'::PAx), 121 | ('B'::PBx), 122 | ('C'::PCx), 123 | ('D'::PDx), 124 | ('E'::PEx) 125 | } 126 | 127 | #[cfg(any(feature = "xl", feature = "high"))] 128 | impl_pxx! { 129 | ('A'::PAx), 130 | ('B'::PBx), 131 | ('C'::PCx), 132 | ('D'::PDx), 133 | ('E'::PEx), 134 | ('F'::PFx), 135 | ('G'::PGx) 136 | } 137 | -------------------------------------------------------------------------------- /src/gpio/hal_02.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use embedded_hal_02::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; 3 | 4 | // Pin 5 | 6 | impl OutputPin for Pin { 7 | type Error = PinModeError; 8 | fn set_high(&mut self) -> Result<(), Self::Error> { 9 | if self.mode.is_output() { 10 | self._set_state(PinState::High); 11 | Ok(()) 12 | } else { 13 | Err(PinModeError::IncorrectMode) 14 | } 15 | } 16 | fn set_low(&mut self) -> Result<(), Self::Error> { 17 | if self.mode.is_output() { 18 | self._set_state(PinState::Low); 19 | Ok(()) 20 | } else { 21 | Err(PinModeError::IncorrectMode) 22 | } 23 | } 24 | } 25 | 26 | impl InputPin for Pin { 27 | type Error = PinModeError; 28 | fn is_high(&self) -> Result { 29 | self.is_low().map(|b| !b) 30 | } 31 | fn is_low(&self) -> Result { 32 | if self.mode.is_input() { 33 | Ok(self._is_low()) 34 | } else { 35 | Err(PinModeError::IncorrectMode) 36 | } 37 | } 38 | } 39 | 40 | impl OutputPin for Pin> { 41 | type Error = Infallible; 42 | #[inline] 43 | fn set_high(&mut self) -> Result<(), Self::Error> { 44 | self.set_high(); 45 | Ok(()) 46 | } 47 | #[inline] 48 | fn set_low(&mut self) -> Result<(), Self::Error> { 49 | self.set_low(); 50 | Ok(()) 51 | } 52 | } 53 | 54 | impl StatefulOutputPin for Pin> { 55 | #[inline] 56 | fn is_set_high(&self) -> Result { 57 | Ok(self.is_set_high()) 58 | } 59 | #[inline] 60 | fn is_set_low(&self) -> Result { 61 | Ok(self.is_set_low()) 62 | } 63 | } 64 | 65 | impl ToggleableOutputPin for Pin> { 66 | type Error = Infallible; 67 | 68 | #[inline(always)] 69 | fn toggle(&mut self) -> Result<(), Self::Error> { 70 | self.toggle(); 71 | Ok(()) 72 | } 73 | } 74 | 75 | impl InputPin for Pin> { 76 | type Error = Infallible; 77 | #[inline] 78 | fn is_high(&self) -> Result { 79 | Ok(self.is_high()) 80 | } 81 | 82 | #[inline] 83 | fn is_low(&self) -> Result { 84 | Ok(self.is_low()) 85 | } 86 | } 87 | 88 | impl InputPin for Pin> { 89 | type Error = Infallible; 90 | #[inline] 91 | fn is_high(&self) -> Result { 92 | Ok(self.is_high()) 93 | } 94 | 95 | #[inline] 96 | fn is_low(&self) -> Result { 97 | Ok(self.is_low()) 98 | } 99 | } 100 | 101 | // PartiallyErasedPin 102 | 103 | impl OutputPin for PartiallyErasedPin> { 104 | type Error = Infallible; 105 | 106 | #[inline(always)] 107 | fn set_high(&mut self) -> Result<(), Self::Error> { 108 | self.set_high(); 109 | Ok(()) 110 | } 111 | 112 | #[inline(always)] 113 | fn set_low(&mut self) -> Result<(), Self::Error> { 114 | self.set_low(); 115 | Ok(()) 116 | } 117 | } 118 | 119 | impl StatefulOutputPin for PartiallyErasedPin> { 120 | #[inline(always)] 121 | fn is_set_high(&self) -> Result { 122 | Ok(self.is_set_high()) 123 | } 124 | 125 | #[inline(always)] 126 | fn is_set_low(&self) -> Result { 127 | Ok(self.is_set_low()) 128 | } 129 | } 130 | 131 | impl ToggleableOutputPin for PartiallyErasedPin> { 132 | type Error = Infallible; 133 | 134 | #[inline(always)] 135 | fn toggle(&mut self) -> Result<(), Self::Error> { 136 | self.toggle(); 137 | Ok(()) 138 | } 139 | } 140 | 141 | impl InputPin for PartiallyErasedPin> { 142 | type Error = Infallible; 143 | 144 | #[inline(always)] 145 | fn is_high(&self) -> Result { 146 | Ok(self.is_high()) 147 | } 148 | 149 | #[inline(always)] 150 | fn is_low(&self) -> Result { 151 | Ok(self.is_low()) 152 | } 153 | } 154 | 155 | impl InputPin for PartiallyErasedPin> { 156 | type Error = Infallible; 157 | 158 | #[inline(always)] 159 | fn is_high(&self) -> Result { 160 | Ok(self.is_high()) 161 | } 162 | 163 | #[inline(always)] 164 | fn is_low(&self) -> Result { 165 | Ok(self.is_low()) 166 | } 167 | } 168 | 169 | // ErasedPin 170 | 171 | impl OutputPin for ErasedPin> { 172 | type Error = Infallible; 173 | fn set_high(&mut self) -> Result<(), Infallible> { 174 | self.set_high(); 175 | Ok(()) 176 | } 177 | 178 | fn set_low(&mut self) -> Result<(), Infallible> { 179 | self.set_low(); 180 | Ok(()) 181 | } 182 | } 183 | 184 | impl StatefulOutputPin for ErasedPin> { 185 | fn is_set_high(&self) -> Result { 186 | Ok(self.is_set_high()) 187 | } 188 | 189 | fn is_set_low(&self) -> Result { 190 | Ok(self.is_set_low()) 191 | } 192 | } 193 | 194 | impl InputPin for ErasedPin> { 195 | type Error = Infallible; 196 | fn is_high(&self) -> Result { 197 | Ok(self.is_high()) 198 | } 199 | 200 | fn is_low(&self) -> Result { 201 | Ok(self.is_low()) 202 | } 203 | } 204 | 205 | impl InputPin for ErasedPin> { 206 | type Error = Infallible; 207 | fn is_high(&self) -> Result { 208 | Ok(self.is_high()) 209 | } 210 | 211 | fn is_low(&self) -> Result { 212 | Ok(self.is_low()) 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/gpio/hal_1.rs: -------------------------------------------------------------------------------- 1 | use core::convert::Infallible; 2 | 3 | use super::{Dynamic, ErasedPin, Input, OpenDrain, Output, PartiallyErasedPin, Pin, PinModeError}; 4 | 5 | pub use embedded_hal::digital::PinState; 6 | use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin}; 7 | 8 | fn into_state(state: PinState) -> super::PinState { 9 | match state { 10 | PinState::Low => super::PinState::Low, 11 | PinState::High => super::PinState::High, 12 | } 13 | } 14 | 15 | // Implementations for `Pin` 16 | impl ErrorType for Pin> { 17 | type Error = Infallible; 18 | } 19 | impl ErrorType for Pin> { 20 | type Error = Infallible; 21 | } 22 | 23 | impl embedded_hal::digital::Error for PinModeError { 24 | fn kind(&self) -> embedded_hal::digital::ErrorKind { 25 | match self { 26 | PinModeError::IncorrectMode => embedded_hal::digital::ErrorKind::Other, 27 | } 28 | } 29 | } 30 | 31 | impl ErrorType for Pin { 32 | type Error = PinModeError; 33 | } 34 | 35 | impl OutputPin for Pin { 36 | fn set_high(&mut self) -> Result<(), Self::Error> { 37 | if self.mode.is_output() { 38 | self._set_state(into_state(PinState::High)); 39 | Ok(()) 40 | } else { 41 | Err(PinModeError::IncorrectMode) 42 | } 43 | } 44 | fn set_low(&mut self) -> Result<(), Self::Error> { 45 | if self.mode.is_output() { 46 | self._set_state(into_state(PinState::Low)); 47 | Ok(()) 48 | } else { 49 | Err(PinModeError::IncorrectMode) 50 | } 51 | } 52 | } 53 | 54 | impl InputPin for Pin { 55 | fn is_high(&mut self) -> Result { 56 | self.is_low().map(|b| !b) 57 | } 58 | fn is_low(&mut self) -> Result { 59 | if self.mode.is_input() { 60 | Ok(self._is_low()) 61 | } else { 62 | Err(PinModeError::IncorrectMode) 63 | } 64 | } 65 | } 66 | 67 | impl OutputPin for Pin> { 68 | #[inline] 69 | fn set_high(&mut self) -> Result<(), Self::Error> { 70 | self.set_high(); 71 | Ok(()) 72 | } 73 | #[inline] 74 | fn set_low(&mut self) -> Result<(), Self::Error> { 75 | self.set_low(); 76 | Ok(()) 77 | } 78 | } 79 | 80 | impl StatefulOutputPin for Pin> { 81 | #[inline] 82 | fn is_set_high(&mut self) -> Result { 83 | Ok((*self).is_set_high()) 84 | } 85 | #[inline] 86 | fn is_set_low(&mut self) -> Result { 87 | Ok((*self).is_set_low()) 88 | } 89 | } 90 | 91 | impl InputPin for Pin> { 92 | #[inline] 93 | fn is_high(&mut self) -> Result { 94 | Ok((*self).is_high()) 95 | } 96 | 97 | #[inline] 98 | fn is_low(&mut self) -> Result { 99 | Ok((*self).is_low()) 100 | } 101 | } 102 | 103 | impl InputPin for Pin> { 104 | #[inline] 105 | fn is_high(&mut self) -> Result { 106 | Ok((*self).is_high()) 107 | } 108 | 109 | #[inline] 110 | fn is_low(&mut self) -> Result { 111 | Ok((*self).is_low()) 112 | } 113 | } 114 | 115 | // PartiallyErasedPin 116 | 117 | impl ErrorType for PartiallyErasedPin { 118 | type Error = Infallible; 119 | } 120 | 121 | impl OutputPin for PartiallyErasedPin> { 122 | #[inline(always)] 123 | fn set_high(&mut self) -> Result<(), Self::Error> { 124 | self.set_high(); 125 | Ok(()) 126 | } 127 | 128 | #[inline(always)] 129 | fn set_low(&mut self) -> Result<(), Self::Error> { 130 | self.set_low(); 131 | Ok(()) 132 | } 133 | } 134 | 135 | impl StatefulOutputPin for PartiallyErasedPin> { 136 | #[inline(always)] 137 | fn is_set_high(&mut self) -> Result { 138 | Ok((*self).is_set_high()) 139 | } 140 | 141 | #[inline(always)] 142 | fn is_set_low(&mut self) -> Result { 143 | Ok((*self).is_set_low()) 144 | } 145 | } 146 | 147 | impl InputPin for PartiallyErasedPin> { 148 | #[inline(always)] 149 | fn is_high(&mut self) -> Result { 150 | Ok((*self).is_high()) 151 | } 152 | 153 | #[inline(always)] 154 | fn is_low(&mut self) -> Result { 155 | Ok((*self).is_low()) 156 | } 157 | } 158 | 159 | impl InputPin for PartiallyErasedPin> { 160 | #[inline(always)] 161 | fn is_high(&mut self) -> Result { 162 | Ok((*self).is_high()) 163 | } 164 | 165 | #[inline(always)] 166 | fn is_low(&mut self) -> Result { 167 | Ok((*self).is_low()) 168 | } 169 | } 170 | 171 | // ErasedPin 172 | 173 | impl ErrorType for ErasedPin { 174 | type Error = core::convert::Infallible; 175 | } 176 | 177 | impl OutputPin for ErasedPin> { 178 | fn set_high(&mut self) -> Result<(), Infallible> { 179 | self.set_high(); 180 | Ok(()) 181 | } 182 | 183 | fn set_low(&mut self) -> Result<(), Infallible> { 184 | self.set_low(); 185 | Ok(()) 186 | } 187 | } 188 | 189 | impl StatefulOutputPin for ErasedPin> { 190 | fn is_set_high(&mut self) -> Result { 191 | Ok((*self).is_set_high()) 192 | } 193 | 194 | fn is_set_low(&mut self) -> Result { 195 | Ok((*self).is_set_low()) 196 | } 197 | } 198 | 199 | impl InputPin for ErasedPin> { 200 | fn is_high(&mut self) -> Result { 201 | Ok((*self).is_high()) 202 | } 203 | 204 | fn is_low(&mut self) -> Result { 205 | Ok((*self).is_low()) 206 | } 207 | } 208 | 209 | impl InputPin for ErasedPin> { 210 | fn is_high(&mut self) -> Result { 211 | Ok((*self).is_high()) 212 | } 213 | 214 | fn is_low(&mut self) -> Result { 215 | Ok((*self).is_low()) 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/gpio/partially_erased.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub type PEPin = PartiallyErasedPin; 4 | 5 | /// Partially erased pin 6 | /// 7 | /// - `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). 8 | /// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc. 9 | pub struct PartiallyErasedPin { 10 | pin_number: u8, 11 | _mode: PhantomData, 12 | } 13 | 14 | impl PartiallyErasedPin { 15 | pub(crate) fn new(pin_number: u8) -> Self { 16 | Self { 17 | pin_number, 18 | _mode: PhantomData, 19 | } 20 | } 21 | } 22 | 23 | impl PinExt for PartiallyErasedPin { 24 | type Mode = MODE; 25 | 26 | #[inline(always)] 27 | fn pin_id(&self) -> u8 { 28 | self.pin_number 29 | } 30 | 31 | #[inline(always)] 32 | fn port_id(&self) -> u8 { 33 | P as u8 - b'A' 34 | } 35 | } 36 | 37 | impl PartiallyErasedPin> { 38 | #[inline(always)] 39 | pub fn set_high(&mut self) { 40 | // NOTE(unsafe) atomic write to a stateless register 41 | let gpio = unsafe { &(*gpiox::

()) }; 42 | gpio.bsrr().write(|w| w.bs(self.pin_number).set_bit()); 43 | } 44 | 45 | #[inline(always)] 46 | pub fn set_low(&mut self) { 47 | // NOTE(unsafe) atomic write to a stateless register 48 | let gpio = unsafe { &(*gpiox::

()) }; 49 | gpio.bsrr().write(|w| w.br(self.pin_number).set_bit()); 50 | } 51 | 52 | #[inline(always)] 53 | pub fn get_state(&self) -> PinState { 54 | if self.is_set_low() { 55 | PinState::Low 56 | } else { 57 | PinState::High 58 | } 59 | } 60 | 61 | #[inline(always)] 62 | pub fn set_state(&mut self, state: PinState) { 63 | match state { 64 | PinState::Low => self.set_low(), 65 | PinState::High => self.set_high(), 66 | } 67 | } 68 | 69 | #[inline(always)] 70 | pub fn is_set_high(&self) -> bool { 71 | !self.is_set_low() 72 | } 73 | 74 | #[inline(always)] 75 | pub fn is_set_low(&self) -> bool { 76 | // NOTE(unsafe) atomic read with no side effects 77 | let gpio = unsafe { &(*gpiox::

()) }; 78 | gpio.odr().read().odr(self.pin_number).bit_is_clear() 79 | } 80 | 81 | #[inline(always)] 82 | pub fn toggle(&mut self) { 83 | if self.is_set_low() { 84 | self.set_high() 85 | } else { 86 | self.set_low() 87 | } 88 | } 89 | } 90 | 91 | impl PartiallyErasedPin> { 92 | #[inline(always)] 93 | pub fn is_high(&self) -> bool { 94 | !self.is_low() 95 | } 96 | 97 | #[inline(always)] 98 | pub fn is_low(&self) -> bool { 99 | // NOTE(unsafe) atomic read with no side effects 100 | let gpio = unsafe { &(*gpiox::

()) }; 101 | gpio.idr().read().idr(self.pin_number).bit_is_clear() 102 | } 103 | } 104 | 105 | impl PartiallyErasedPin> { 106 | #[inline(always)] 107 | pub fn is_high(&self) -> bool { 108 | !self.is_low() 109 | } 110 | 111 | #[inline(always)] 112 | pub fn is_low(&self) -> bool { 113 | // NOTE(unsafe) atomic read with no side effects 114 | let gpio = unsafe { &(*gpiox::

()) }; 115 | gpio.idr().read().idr(self.pin_number).bit_is_clear() 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/i2c/hal_02.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use embedded_hal_02::blocking::i2c::{Operation, Read, Transactional, Write, WriteRead}; 3 | 4 | impl Write for BlockingI2c { 5 | type Error = Error; 6 | 7 | fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { 8 | self.write(addr, bytes) 9 | } 10 | } 11 | 12 | impl Read for BlockingI2c { 13 | type Error = Error; 14 | 15 | fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 16 | self.read(addr, buffer) 17 | } 18 | } 19 | 20 | impl WriteRead for BlockingI2c { 21 | type Error = Error; 22 | 23 | fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { 24 | self.write_read(addr, bytes, buffer) 25 | } 26 | } 27 | 28 | impl Transactional for BlockingI2c { 29 | type Error = Error; 30 | 31 | fn exec(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { 32 | self.transaction_slice_hal_02(address, operations) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/i2c/hal_1.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::i2c::{Error, ErrorKind, ErrorType}; 2 | 3 | impl Error for super::Error { 4 | fn kind(&self) -> ErrorKind { 5 | match *self { 6 | Self::Overrun => ErrorKind::Overrun, 7 | Self::Bus => ErrorKind::Bus, 8 | Self::ArbitrationLoss => ErrorKind::ArbitrationLoss, 9 | Self::NoAcknowledge(nack) => ErrorKind::NoAcknowledge(nack), 10 | Self::Timeout => ErrorKind::Other, 11 | } 12 | } 13 | } 14 | 15 | impl ErrorType for super::BlockingI2c { 16 | type Error = super::Error; 17 | } 18 | 19 | mod blocking { 20 | use super::super::{BlockingI2c, Instance}; 21 | use embedded_hal::i2c::Operation; 22 | 23 | impl embedded_hal::i2c::I2c for BlockingI2c { 24 | fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 25 | self.read(addr, buffer) 26 | } 27 | 28 | fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { 29 | self.write(addr, bytes) 30 | } 31 | 32 | fn write_read( 33 | &mut self, 34 | addr: u8, 35 | bytes: &[u8], 36 | buffer: &mut [u8], 37 | ) -> Result<(), Self::Error> { 38 | self.write_read(addr, bytes, buffer) 39 | } 40 | 41 | fn transaction( 42 | &mut self, 43 | addr: u8, 44 | operations: &mut [Operation<'_>], 45 | ) -> Result<(), Self::Error> { 46 | self.transaction_slice(addr, operations) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # HAL for the STM32F1 family of microcontrollers 2 | //! 3 | //! This is an implementation of the [`embedded-hal`] traits for the STM32F1 family of 4 | //! microcontrollers. 5 | //! 6 | //! [`embedded-hal`]: https://crates.io/crates/embedded-hal 7 | //! 8 | //! # Usage 9 | //! 10 | //! ## Building an application (binary crate) 11 | //! 12 | //! A detailed usage guide can be found in the [README] 13 | //! 14 | //! supported microcontrollers are: 15 | //! 16 | //! - stm32f103 17 | //! - stm32f101 18 | //! - stm32f100 19 | //! - stm32f105 20 | //! - stm32f107 21 | //! 22 | //! ## Usage 23 | //! 24 | //! This crate supports multiple microcontrollers in the 25 | //! stm32f1 family. Which specific microcontroller you want to build for has to be 26 | //! specified with a feature, for example `stm32f103`. 27 | //! 28 | //! If no microcontroller is specified, the crate will not compile. 29 | //! 30 | //! The currently supported variants are 31 | //! 32 | //! - `stm32f100` 33 | //! - `stm32f101` 34 | //! - `stm32f103` 35 | //! - `stm32f105` 36 | //! - `stm32f107` 37 | //! 38 | //! You may also need to specify the density of the device with `medium`, `high` or `xl` 39 | //! to enable certain peripherals. Generally the density can be determined by the 2nd character 40 | //! after the number in the device name (i.e. For STM32F103C6U, the 6 indicates a low-density 41 | //! device) but check the datasheet or CubeMX to be sure. 42 | //! * 4, 6 => low density, no feature required 43 | //! * 8, B => `medium` feature 44 | //! * C, D, E => `high` feature 45 | //! * F, G => `xl` feature 46 | //! 47 | //! ## Commonly used setup 48 | //! Almost all peripherals require references to some registers in `RCC` and `AFIO`. The following 49 | //! code shows how to set up those registers 50 | //! 51 | //! ```rust 52 | //! // Get access to the device specific peripherals from the peripheral access crate 53 | //! let dp = pac::Peripherals::take().unwrap(); 54 | //! 55 | //! // Take ownership over the raw flash and rcc devices and convert them into the corresponding 56 | //! // HAL structs 57 | //! let mut flash = dp.FLASH.constrain(); 58 | //! let mut rcc = dp.RCC.constrain(); 59 | //! 60 | //! // Freeze the configuration of all the clocks in the system and store the frozen frequencies in 61 | //! // `clocks` 62 | //! let clocks = rcc.cfgr.freeze(&mut flash.acr); 63 | //! 64 | //! // Prepare the alternate function I/O registers 65 | //! let mut afio = dp.AFIO.constrain(); 66 | //! ``` 67 | //! 68 | //! ## Usage examples 69 | //! 70 | //! See the [examples] folder. 71 | //! 72 | //! Most of the examples require the following additional dependencies 73 | //! ```toml 74 | //! [dependencies] 75 | //! embedded-hal = "0.2.3" 76 | //! nb = "0.1.2" 77 | //! cortex-m = "0.6.2" 78 | //! cortex-m-rt = "0.6.11" 79 | //! # Panic behaviour, see https://crates.io/keywords/panic-impl for alternatives 80 | //! panic-halt = "0.2.0" 81 | //! ``` 82 | //! 83 | //! [examples]: https://github.com/stm32-rs/stm32f1xx-hal/tree/v0.7.0/examples 84 | //! [README]: https://github.com/stm32-rs/stm32f1xx-hal/tree/v0.7.0 85 | 86 | #![no_std] 87 | #![deny(rustdoc::broken_intra_doc_links)] 88 | 89 | // If no target specified, print error message. 90 | #[cfg(not(any( 91 | feature = "stm32f100", 92 | feature = "stm32f101", 93 | feature = "stm32f103", 94 | feature = "stm32f105", 95 | feature = "stm32f107", 96 | )))] 97 | compile_error!("Target not found. A `--features ` is required."); 98 | 99 | // If any two or more targets are specified, print error message. 100 | #[cfg(any( 101 | all(feature = "stm32f100", feature = "stm32f101"), 102 | all(feature = "stm32f100", feature = "stm32f103"), 103 | all(feature = "stm32f100", feature = "stm32f105"), 104 | all(feature = "stm32f100", feature = "stm32f107"), 105 | all(feature = "stm32f101", feature = "stm32f103"), 106 | all(feature = "stm32f101", feature = "stm32f105"), 107 | all(feature = "stm32f101", feature = "stm32f107"), 108 | all(feature = "stm32f103", feature = "stm32f105"), 109 | all(feature = "stm32f103", feature = "stm32f107"), 110 | all(feature = "stm32f105", feature = "stm32f107"), 111 | ))] 112 | compile_error!( 113 | "Multiple targets specified. Only a single `--features ` can be specified." 114 | ); 115 | 116 | pub mod pacext; 117 | 118 | pub use embedded_hal as hal; 119 | pub use embedded_hal_02 as hal_02; 120 | 121 | #[cfg(feature = "stm32f100")] 122 | pub use stm32f1::stm32f100 as pac; 123 | 124 | #[cfg(feature = "stm32f101")] 125 | pub use stm32f1::stm32f101 as pac; 126 | 127 | #[cfg(feature = "stm32f103")] 128 | pub use stm32f1::stm32f103 as pac; 129 | 130 | #[cfg(any(feature = "stm32f105", feature = "stm32f107"))] 131 | pub use stm32f1::stm32f107 as pac; 132 | 133 | pub mod adc; 134 | pub mod afio; 135 | pub mod backup_domain; 136 | pub mod bb; 137 | #[cfg(feature = "has-can")] 138 | pub mod can; 139 | pub mod crc; 140 | #[cfg(feature = "has-dac")] 141 | pub mod dac; 142 | pub mod dma; 143 | pub mod flash; 144 | pub mod gpio; 145 | pub mod i2c; 146 | pub mod prelude; 147 | pub mod rcc; 148 | pub mod rtc; 149 | pub mod serial; 150 | pub mod spi; 151 | pub mod time; 152 | pub mod timer; 153 | #[cfg(feature = "stm32-usbd")] 154 | #[cfg(feature = "stm32f103")] 155 | pub mod usb; 156 | pub mod watchdog; 157 | 158 | mod sealed { 159 | pub trait Sealed {} 160 | } 161 | use sealed::Sealed; 162 | use stm32f1::Periph; 163 | 164 | impl Sealed for Periph {} 165 | 166 | pub trait Ptr: Sealed { 167 | /// RegisterBlock structure 168 | type RB; 169 | /// Return the pointer to the register block 170 | fn ptr() -> *const Self::RB; 171 | } 172 | 173 | impl Ptr for Periph { 174 | type RB = RB; 175 | fn ptr() -> *const Self::RB { 176 | Self::ptr() 177 | } 178 | } 179 | 180 | pub trait Steal: Sealed { 181 | /// Steal an instance of this peripheral 182 | /// 183 | /// # Safety 184 | /// 185 | /// Ensure that the new instance of the peripheral cannot be used in a way 186 | /// that may race with any existing instances, for example by only 187 | /// accessing read-only or write-only registers, or by consuming the 188 | /// original peripheral and using critical sections to coordinate 189 | /// access between multiple new instances. 190 | /// 191 | /// Additionally the HAL may rely on only one 192 | /// peripheral instance existing to ensure memory safety; ensure 193 | /// no stolen instances are passed to such software. 194 | unsafe fn steal() -> Self; 195 | } 196 | 197 | impl Steal for Periph { 198 | unsafe fn steal() -> Self { 199 | Self::steal() 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/pacext.rs: -------------------------------------------------------------------------------- 1 | use stm32f1::{Readable, Reg, RegisterSpec, Resettable, Writable, R, W}; 2 | 3 | pub mod adc; 4 | pub mod uart; 5 | 6 | macro_rules! wrap_r { 7 | (pub trait $TrR:ident { 8 | $(fn $f:ident(&self $(, $n:ident: u8)?) -> $fr:path;)* 9 | }) => { 10 | pub trait $TrR { 11 | $(fn $f(&self $(, $n: u8)?) -> $fr;)* 12 | } 13 | impl $TrR for R { 14 | $( 15 | #[inline(always)] 16 | fn $f(&self $(, $n: u8)?) -> $fr { 17 | REG::$f(self $(, $n)?) 18 | } 19 | )* 20 | } 21 | }; 22 | } 23 | pub(crate) use wrap_r; 24 | 25 | macro_rules! wrap_w { 26 | (pub trait $TrR:ident { 27 | $(fn $f:ident(&mut self $(, $n:ident: u8)?) -> $fr:path;)* 28 | }) => { 29 | pub trait $TrR { 30 | $(fn $f(&mut self $(, $n: u8)?) -> $fr;)* 31 | } 32 | 33 | impl $TrR for W { 34 | $( 35 | #[inline(always)] 36 | fn $f(&mut self $(, $n: u8)?) -> $fr { 37 | REG::$f(self $(, $n)?) 38 | } 39 | )* 40 | } 41 | }; 42 | } 43 | pub(crate) use wrap_w; 44 | 45 | macro_rules! impl_reg { 46 | ($($r:ident $(: $n:ident)? -> &$rty:path;)*) => { 47 | $( 48 | #[inline(always)] 49 | fn $r(&self $(, $n: usize)?) -> &$rty { 50 | self.$r($($n)?) 51 | } 52 | )* 53 | }; 54 | } 55 | pub(crate) use impl_reg; 56 | 57 | macro_rules! impl_read { 58 | ($($f:ident $(: $n:ident)? -> $fty:path;)*) => { 59 | $( 60 | #[inline(always)] 61 | fn $f(r: &R $(, $n: u8)?) -> $fty { 62 | r.$f($($n)?) 63 | } 64 | )* 65 | }; 66 | } 67 | pub(crate) use impl_read; 68 | 69 | macro_rules! impl_write { 70 | ($($f:ident $(: $n:ident)? -> $fty:path;)*) => { 71 | $( 72 | #[inline(always)] 73 | fn $f(w: &mut W $(, $n: u8)?) -> $fty { 74 | w.$f($($n)?) 75 | } 76 | )* 77 | }; 78 | } 79 | pub(crate) use impl_write; 80 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::adc::AdcExt as _; 2 | pub use crate::adc::ChannelTimeSequence as _stm32_hal_adc_ChannelTimeSequence; 3 | pub use crate::afio::AfioExt as _stm32_hal_afio_AfioExt; 4 | pub use crate::afio::RFrom as _; 5 | pub use crate::afio::RInto as _; 6 | pub use crate::afio::Remap as _; 7 | #[cfg(feature = "has-can")] 8 | pub use crate::can::CanExt as _; 9 | pub use crate::crc::CrcExt as _stm32_hal_crc_CrcExt; 10 | pub use crate::dma::CircReadDma as _stm32_hal_dma_CircReadDma; 11 | pub use crate::dma::DmaExt as _stm32_hal_dma_DmaExt; 12 | pub use crate::dma::ReadDma as _stm32_hal_dma_ReadDma; 13 | pub use crate::dma::ReadWriteDma as _stm32_hal_dma_ReadWriteDma; 14 | pub use crate::dma::WriteDma as _stm32_hal_dma_WriteDma; 15 | pub use crate::flash::FlashExt as _stm32_hal_flash_FlashExt; 16 | pub use crate::gpio::GpioExt as _stm32_hal_gpio_GpioExt; 17 | pub use crate::hal_02::adc::OneShot as _embedded_hal_adc_OneShot; 18 | pub use crate::hal_02::prelude::*; 19 | pub use crate::i2c::I2cExt as _; 20 | pub use crate::rcc::RccExt as _stm32_hal_rcc_RccExt; 21 | pub use crate::serial::SerialExt as _; 22 | pub use crate::spi::SpiExt as _; 23 | pub use crate::time::U32Ext as _stm32_hal_time_U32Ext; 24 | pub use crate::timer::pwm_input::PwmInputExt as _; 25 | pub use crate::timer::pwm_input::QeiExt as _; 26 | #[cfg(feature = "rtic")] 27 | pub use crate::timer::MonoTimerExt as _stm32f4xx_hal_timer_MonoTimerExt; 28 | pub use crate::timer::PwmExt as _stm32f4xx_hal_timer_PwmExt; 29 | pub use crate::timer::SysTimerExt as _stm32f4xx_hal_timer_SysCounterExt; 30 | pub use crate::timer::TimerExt as _stm32f4xx_hal_timer_TimerExt; 31 | pub use fugit::ExtU32 as _fugit_ExtU32; 32 | pub use fugit::RateExtU32 as _fugit_RateExtU32; 33 | -------------------------------------------------------------------------------- /src/rcc/enable.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::bb; 3 | 4 | macro_rules! bus { 5 | ($($PER:ident => ($apbX:ty, $bit:literal),)+) => { 6 | $( 7 | impl RccBus for crate::pac::$PER { 8 | type Bus = $apbX; 9 | } 10 | impl Enable for crate::pac::$PER { 11 | #[inline(always)] 12 | fn enable(rcc: &rcc::RegisterBlock) { 13 | unsafe { 14 | bb::set(Self::Bus::enr(rcc), $bit); 15 | } 16 | } 17 | #[inline(always)] 18 | fn disable(rcc: &rcc::RegisterBlock) { 19 | unsafe { 20 | bb::clear(Self::Bus::enr(rcc), $bit); 21 | } 22 | } 23 | } 24 | impl Reset for crate::pac::$PER { 25 | #[inline(always)] 26 | fn reset(rcc: &rcc::RegisterBlock) { 27 | unsafe { 28 | bb::set(Self::Bus::rstr(rcc), $bit); 29 | bb::clear(Self::Bus::rstr(rcc), $bit); 30 | } 31 | } 32 | } 33 | )+ 34 | } 35 | } 36 | 37 | macro_rules! ahb_bus { 38 | ($($PER:ident => ($bit:literal),)+) => { 39 | $( 40 | impl RccBus for crate::pac::$PER { 41 | type Bus = AHB; 42 | } 43 | impl Enable for crate::pac::$PER { 44 | #[inline(always)] 45 | fn enable(rcc: &rcc::RegisterBlock) { 46 | unsafe { 47 | bb::set(Self::Bus::enr(rcc), $bit); 48 | } 49 | } 50 | #[inline(always)] 51 | fn disable(rcc: &rcc::RegisterBlock) { 52 | unsafe { 53 | bb::clear(Self::Bus::enr(rcc), $bit); 54 | } 55 | } 56 | } 57 | )+ 58 | } 59 | } 60 | 61 | #[cfg(feature = "stm32f103")] 62 | bus! { 63 | ADC2 => (APB2, 10), 64 | CAN => (APB1, 25), 65 | } 66 | #[cfg(feature = "connectivity")] 67 | bus! { 68 | ADC2 => (APB2, 10), 69 | CAN1 => (APB1, 25), 70 | CAN2 => (APB1, 26), 71 | } 72 | #[cfg(feature = "has-dac")] 73 | bus! { 74 | DAC => (APB1, 29), 75 | } 76 | #[cfg(any(all(feature = "stm32f103", feature = "high"), feature = "connectivity"))] 77 | bus! { 78 | ADC3 => (APB2, 15), 79 | UART4 => (APB1, 19), 80 | UART5 => (APB1, 20), 81 | } 82 | bus! { 83 | ADC1 => (APB2, 9), 84 | AFIO => (APB2, 0), 85 | BKP => (APB1, 27), 86 | GPIOA => (APB2, 2), 87 | GPIOB => (APB2, 3), 88 | GPIOC => (APB2, 4), 89 | GPIOD => (APB2, 5), 90 | GPIOE => (APB2, 6), 91 | I2C1 => (APB1, 21), 92 | I2C2 => (APB1, 22), 93 | PWR => (APB1, 28), 94 | SPI1 => (APB2, 12), 95 | SPI2 => (APB1, 14), 96 | USART1 => (APB2, 14), 97 | USART2 => (APB1, 17), 98 | USART3 => (APB1, 18), 99 | WWDG => (APB1, 11), 100 | } 101 | 102 | #[cfg(any(feature = "xl", feature = "high"))] 103 | bus! { 104 | GPIOF => (APB2, 7), 105 | GPIOG => (APB2, 8), 106 | } 107 | 108 | #[cfg(any(feature = "high", feature = "connectivity"))] 109 | bus! { 110 | SPI3 => (APB1, 15), 111 | } 112 | 113 | ahb_bus! { 114 | CRC => (6), 115 | DMA1 => (0), 116 | DMA2 => (1), 117 | } 118 | 119 | #[cfg(feature = "high")] 120 | ahb_bus! { 121 | FSMC => (8), 122 | } 123 | 124 | bus! { 125 | TIM2 => (APB1, 0), 126 | TIM3 => (APB1, 1), 127 | } 128 | 129 | #[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))] 130 | bus! { 131 | TIM1 => (APB2, 11), 132 | } 133 | 134 | #[cfg(any(feature = "stm32f100", feature = "high", feature = "connectivity"))] 135 | bus! { 136 | TIM6 => (APB1, 4), 137 | } 138 | 139 | #[cfg(any( 140 | all(feature = "high", any(feature = "stm32f101", feature = "stm32f103")), 141 | any(feature = "stm32f100", feature = "connectivity") 142 | ))] 143 | bus! { 144 | TIM7 => (APB1, 5), 145 | } 146 | 147 | #[cfg(feature = "stm32f100")] 148 | bus! { 149 | TIM15 => (APB2, 16), 150 | TIM16 => (APB2, 17), 151 | TIM17 => (APB2, 18), 152 | } 153 | 154 | #[cfg(feature = "medium")] 155 | bus! { 156 | TIM4 => (APB1, 2), 157 | } 158 | 159 | #[cfg(any(feature = "high", feature = "connectivity"))] 160 | bus! { 161 | TIM5 => (APB1, 3), 162 | } 163 | 164 | #[cfg(any(feature = "xl", all(feature = "stm32f100", feature = "high")))] 165 | bus! { 166 | TIM12 => (APB1, 6), 167 | TIM13 => (APB1, 7), 168 | TIM14 => (APB1, 8), 169 | } 170 | 171 | #[cfg(all(feature = "stm32f103", feature = "high"))] 172 | bus! { 173 | TIM8 => (APB2, 13), 174 | } 175 | 176 | #[cfg(feature = "xl")] 177 | bus! { 178 | TIM9 => (APB2, 19), 179 | TIM10 => (APB2, 20), 180 | TIM11 => (APB2, 21), 181 | } 182 | 183 | #[cfg(feature = "stm32f103")] // feature = "stm32f102" 184 | bus! { 185 | USB => (APB1, 23), 186 | } 187 | -------------------------------------------------------------------------------- /src/serial/hal_02.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use embedded_hal_02::{blocking::serial as blocking, serial}; 3 | 4 | impl serial::Write for Tx { 5 | type Error = Error; 6 | 7 | fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { 8 | self.write_u8(word) 9 | } 10 | 11 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 12 | self.flush() 13 | } 14 | } 15 | 16 | impl serial::Write for Tx { 17 | type Error = Error; 18 | 19 | fn write(&mut self, word: u16) -> nb::Result<(), Self::Error> { 20 | self.write_u16(word) 21 | } 22 | 23 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 24 | self.flush() 25 | } 26 | } 27 | 28 | impl serial::Read for Rx { 29 | type Error = Error; 30 | 31 | fn read(&mut self) -> nb::Result { 32 | self.read() 33 | } 34 | } 35 | 36 | impl serial::Read for Rx { 37 | type Error = Error; 38 | 39 | fn read(&mut self) -> nb::Result { 40 | self.read_u16() 41 | } 42 | } 43 | 44 | impl serial::Write for Serial { 45 | type Error = Error; 46 | 47 | fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { 48 | self.tx.write_u8(word) 49 | } 50 | 51 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 52 | self.tx.flush() 53 | } 54 | } 55 | 56 | impl serial::Write for Serial { 57 | type Error = Error; 58 | 59 | fn write(&mut self, word: u16) -> nb::Result<(), Self::Error> { 60 | self.tx.write_u16(word) 61 | } 62 | 63 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 64 | self.tx.flush() 65 | } 66 | } 67 | 68 | impl serial::Read for Serial { 69 | type Error = Error; 70 | 71 | fn read(&mut self) -> nb::Result { 72 | self.rx.read() 73 | } 74 | } 75 | 76 | impl serial::Read for Serial { 77 | type Error = Error; 78 | 79 | fn read(&mut self) -> nb::Result { 80 | self.rx.read_u16() 81 | } 82 | } 83 | 84 | // Blocking 85 | 86 | impl blocking::Write for Tx { 87 | type Error = Error; 88 | 89 | fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { 90 | self.bwrite_all_u8(buffer) 91 | } 92 | 93 | fn bflush(&mut self) -> Result<(), Self::Error> { 94 | self.bflush() 95 | } 96 | } 97 | 98 | impl blocking::Write for Tx { 99 | type Error = Error; 100 | 101 | fn bwrite_all(&mut self, buffer: &[u16]) -> Result<(), Self::Error> { 102 | self.bwrite_all_u16(buffer) 103 | } 104 | 105 | fn bflush(&mut self) -> Result<(), Self::Error> { 106 | self.bflush() 107 | } 108 | } 109 | 110 | impl blocking::Write for Serial { 111 | type Error = Error; 112 | 113 | fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { 114 | self.tx.bwrite_all_u8(buffer) 115 | } 116 | 117 | fn bflush(&mut self) -> Result<(), Self::Error> { 118 | self.tx.bflush() 119 | } 120 | } 121 | 122 | impl blocking::Write for Serial { 123 | type Error = Error; 124 | 125 | fn bwrite_all(&mut self, buffer: &[u16]) -> Result<(), Self::Error> { 126 | self.tx.bwrite_all_u16(buffer) 127 | } 128 | 129 | fn bflush(&mut self) -> Result<(), Self::Error> { 130 | self.tx.bflush() 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/serial/hal_1.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | mod nb { 4 | use super::{Error, Instance, Rx, Serial, Tx}; 5 | use embedded_hal_nb::serial::ErrorKind; 6 | use embedded_hal_nb::{serial, serial::ErrorType}; 7 | 8 | impl embedded_hal_nb::serial::Error for Error { 9 | fn kind(&self) -> ErrorKind { 10 | match self { 11 | Error::Overrun => ErrorKind::Overrun, 12 | Error::FrameFormat => ErrorKind::FrameFormat, 13 | Error::Parity => ErrorKind::Parity, 14 | Error::Noise => ErrorKind::Noise, 15 | Error::Other => ErrorKind::Other, 16 | } 17 | } 18 | } 19 | 20 | impl ErrorType for Tx { 21 | type Error = Error; 22 | } 23 | 24 | impl ErrorType for Rx { 25 | type Error = Error; 26 | } 27 | 28 | impl ErrorType for Serial { 29 | type Error = Error; 30 | } 31 | 32 | impl serial::Write for Tx { 33 | fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { 34 | self.write_u8(word)?; 35 | Ok(()) 36 | } 37 | 38 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 39 | self.flush() 40 | } 41 | } 42 | 43 | impl serial::Write for Tx { 44 | fn write(&mut self, word: u16) -> nb::Result<(), Self::Error> { 45 | self.write_u16(word) 46 | } 47 | 48 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 49 | self.flush() 50 | } 51 | } 52 | 53 | impl serial::Read for Rx { 54 | fn read(&mut self) -> nb::Result { 55 | self.read() 56 | } 57 | } 58 | 59 | impl serial::Read for Rx { 60 | fn read(&mut self) -> nb::Result { 61 | self.read_u16() 62 | } 63 | } 64 | 65 | impl serial::Write for Serial { 66 | fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { 67 | self.tx.write_u8(word).unwrap(); 68 | Ok(()) 69 | } 70 | 71 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 72 | self.tx.flush().unwrap(); 73 | Ok(()) 74 | } 75 | } 76 | 77 | impl serial::Write for Serial { 78 | fn write(&mut self, word: u16) -> nb::Result<(), Self::Error> { 79 | self.tx.write_u16(word).unwrap(); 80 | Ok(()) 81 | } 82 | 83 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 84 | self.tx.flush().unwrap(); 85 | Ok(()) 86 | } 87 | } 88 | 89 | impl serial::Read for Serial { 90 | fn read(&mut self) -> nb::Result { 91 | self.rx.read() 92 | } 93 | } 94 | 95 | impl serial::Read for Serial { 96 | fn read(&mut self) -> nb::Result { 97 | self.rx.read_u16() 98 | } 99 | } 100 | } 101 | 102 | mod io { 103 | use super::super::{Error, Instance, Rx, Serial, Tx}; 104 | use embedded_io::Write; 105 | 106 | impl embedded_io::Error for Error { 107 | // TODO: fix error conversion 108 | fn kind(&self) -> embedded_io::ErrorKind { 109 | embedded_io::ErrorKind::Other 110 | } 111 | } 112 | 113 | impl embedded_io::ErrorType for Serial { 114 | type Error = Error; 115 | } 116 | 117 | impl embedded_io::ErrorType for Tx { 118 | type Error = Error; 119 | } 120 | 121 | impl embedded_io::ErrorType for Rx { 122 | type Error = Error; 123 | } 124 | 125 | impl Write for Tx { 126 | fn write(&mut self, bytes: &[u8]) -> Result { 127 | let mut i = 0; 128 | for byte in bytes.iter() { 129 | match self.write_u8(*byte) { 130 | Ok(_) => { 131 | i += 1; 132 | } 133 | Err(nb::Error::WouldBlock) => { 134 | return Ok(i); 135 | } 136 | Err(nb::Error::Other(e)) => { 137 | return Err(e); 138 | } 139 | } 140 | } 141 | Ok(i) 142 | } 143 | 144 | fn flush(&mut self) -> Result<(), Self::Error> { 145 | self.bflush()?; 146 | Ok(()) 147 | } 148 | } 149 | 150 | impl Write for Serial 151 | where 152 | Tx: Write, 153 | { 154 | fn write(&mut self, bytes: &[u8]) -> Result { 155 | self.tx.write(bytes) 156 | } 157 | 158 | fn flush(&mut self) -> Result<(), Self::Error> { 159 | Write::flush(&mut self.tx) 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/spi/hal_02.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub use embedded_hal_02::spi::{Mode, Phase, Polarity}; 4 | use embedded_hal_02::{blocking::spi as blocking, spi}; 5 | 6 | impl From for super::Polarity { 7 | fn from(p: Polarity) -> Self { 8 | match p { 9 | Polarity::IdleLow => Self::IdleLow, 10 | Polarity::IdleHigh => Self::IdleHigh, 11 | } 12 | } 13 | } 14 | 15 | impl From for super::Phase { 16 | fn from(p: Phase) -> Self { 17 | match p { 18 | Phase::CaptureOnFirstTransition => Self::CaptureOnFirstTransition, 19 | Phase::CaptureOnSecondTransition => Self::CaptureOnSecondTransition, 20 | } 21 | } 22 | } 23 | 24 | impl From for super::Mode { 25 | fn from(m: Mode) -> Self { 26 | Self { 27 | polarity: m.polarity.into(), 28 | phase: m.phase.into(), 29 | } 30 | } 31 | } 32 | 33 | impl spi::FullDuplex for Spi 34 | where 35 | SPI: Instance, 36 | W: Copy, 37 | { 38 | type Error = Error; 39 | 40 | fn read(&mut self) -> nb::Result { 41 | self.read_nonblocking() 42 | } 43 | 44 | fn send(&mut self, data: W) -> nb::Result<(), Error> { 45 | self.write_nonblocking(data) 46 | } 47 | } 48 | 49 | impl blocking::transfer::Default for Spi 50 | where 51 | SPI: Instance, 52 | W: Copy, 53 | { 54 | } 55 | 56 | impl blocking::Write for Spi { 57 | type Error = Error; 58 | 59 | fn write(&mut self, words: &[u8]) -> Result<(), Error> { 60 | self.deref_mut().write(words) 61 | } 62 | } 63 | 64 | impl blocking::Write for Spi { 65 | type Error = Error; 66 | 67 | fn write(&mut self, words: &[u16]) -> Result<(), Error> { 68 | self.deref_mut().write(words) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/spi/hal_1.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | pub use embedded_hal::spi::{ErrorKind, ErrorType, Mode, Phase, Polarity}; 3 | 4 | impl From for super::Polarity { 5 | fn from(p: Polarity) -> Self { 6 | match p { 7 | Polarity::IdleLow => Self::IdleLow, 8 | Polarity::IdleHigh => Self::IdleHigh, 9 | } 10 | } 11 | } 12 | 13 | impl From for super::Phase { 14 | fn from(p: Phase) -> Self { 15 | match p { 16 | Phase::CaptureOnFirstTransition => Self::CaptureOnFirstTransition, 17 | Phase::CaptureOnSecondTransition => Self::CaptureOnSecondTransition, 18 | } 19 | } 20 | } 21 | 22 | impl From for super::Mode { 23 | fn from(m: Mode) -> Self { 24 | Self { 25 | polarity: m.polarity.into(), 26 | phase: m.phase.into(), 27 | } 28 | } 29 | } 30 | 31 | impl embedded_hal::spi::Error for Error { 32 | fn kind(&self) -> ErrorKind { 33 | match self { 34 | Self::Overrun => ErrorKind::Overrun, 35 | Self::ModeFault => ErrorKind::ModeFault, 36 | Self::Crc => ErrorKind::Other, 37 | } 38 | } 39 | } 40 | 41 | impl ErrorType for Spi { 42 | type Error = Error; 43 | } 44 | 45 | mod nb { 46 | use super::{Error, Instance, Spi}; 47 | use embedded_hal_nb::spi::FullDuplex; 48 | 49 | impl FullDuplex for Spi 50 | where 51 | SPI: Instance, 52 | W: Copy, 53 | { 54 | fn read(&mut self) -> nb::Result { 55 | self.read_nonblocking() 56 | } 57 | 58 | fn write(&mut self, data: W) -> nb::Result<(), Error> { 59 | self.write_nonblocking(data) 60 | } 61 | } 62 | } 63 | 64 | mod blocking { 65 | use super::super::{Instance, Spi}; 66 | use core::ops::DerefMut; 67 | use embedded_hal::spi::SpiBus; 68 | 69 | impl SpiBus for Spi 70 | where 71 | SPI: Instance, 72 | W: Copy + 'static, 73 | { 74 | fn transfer_in_place(&mut self, _words: &mut [W]) -> Result<(), Self::Error> { 75 | todo!() 76 | } 77 | 78 | fn transfer(&mut self, _buff: &mut [W], _data: &[W]) -> Result<(), Self::Error> { 79 | todo!() 80 | } 81 | 82 | fn read(&mut self, _words: &mut [W]) -> Result<(), Self::Error> { 83 | todo!() 84 | } 85 | 86 | fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { 87 | self.deref_mut().write(words) 88 | } 89 | 90 | fn flush(&mut self) -> Result<(), Self::Error> { 91 | Ok(()) 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/time.rs: -------------------------------------------------------------------------------- 1 | //! Time units 2 | //! 3 | //! See [`Hertz`], [`KiloHertz`] and [`MegaHertz`] for creating increasingly higher frequencies. 4 | //! 5 | //! The [`fugit::ExtU32`] [`U32Ext`] trait adds various methods like `.Hz()`, `.MHz()`, etc to the `u32` primitive type, 6 | //! allowing it to be converted into frequencies. 7 | //! 8 | //! # Examples 9 | //! 10 | //! ## Create a 2 MHz frequency 11 | //! 12 | //! This example demonstrates various ways of creating a 2 MHz (2_000_000 Hz) frequency. They are 13 | //! all equivalent, however the `2.MHz()` variant should be preferred for readability. 14 | //! 15 | //! ```rust 16 | //! use stm32f1xx_hal::{ 17 | //! time::Hertz, 18 | //! // Imports U32Ext trait 19 | //! prelude::*, 20 | //! }; 21 | //! 22 | //! let freq_hz = 2_000_000.Hz(); 23 | //! let freq_khz = 2_000.kHz(); 24 | //! let freq_mhz = 2.MHz(); 25 | //! 26 | //! assert_eq!(freq_hz, freq_khz); 27 | //! assert_eq!(freq_khz, freq_mhz); 28 | //! ``` 29 | 30 | #![allow(non_snake_case)] 31 | 32 | use core::ops; 33 | use cortex_m::peripheral::{DCB, DWT}; 34 | 35 | use crate::rcc::Clocks; 36 | 37 | /// Bits per second 38 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Debug)] 39 | pub struct Bps(pub u32); 40 | 41 | pub use fugit::{ 42 | HertzU32 as Hertz, KilohertzU32 as KiloHertz, MegahertzU32 as MegaHertz, 43 | MicrosDurationU32 as MicroSeconds, MillisDurationU32 as MilliSeconds, 44 | }; 45 | 46 | /// Extension trait that adds convenience methods to the `u32` type 47 | pub trait U32Ext { 48 | /// Wrap in `Bps` 49 | fn bps(self) -> Bps; 50 | } 51 | 52 | impl U32Ext for u32 { 53 | fn bps(self) -> Bps { 54 | Bps(self) 55 | } 56 | } 57 | 58 | pub const fn Hz(val: u32) -> Hertz { 59 | Hertz::from_raw(val) 60 | } 61 | 62 | pub const fn kHz(val: u32) -> KiloHertz { 63 | KiloHertz::from_raw(val) 64 | } 65 | 66 | pub const fn MHz(val: u32) -> MegaHertz { 67 | MegaHertz::from_raw(val) 68 | } 69 | 70 | pub const fn ms(val: u32) -> MilliSeconds { 71 | MilliSeconds::from_ticks(val) 72 | } 73 | 74 | pub const fn us(val: u32) -> MicroSeconds { 75 | MicroSeconds::from_ticks(val) 76 | } 77 | 78 | /// Macro to implement arithmetic operations (e.g. multiplication, division) 79 | /// for wrapper types. 80 | macro_rules! impl_arithmetic { 81 | ($wrapper:ty, $wrapped:ty) => { 82 | impl ops::Mul<$wrapped> for $wrapper { 83 | type Output = Self; 84 | fn mul(self, rhs: $wrapped) -> Self { 85 | Self(self.0 * rhs) 86 | } 87 | } 88 | 89 | impl ops::MulAssign<$wrapped> for $wrapper { 90 | fn mul_assign(&mut self, rhs: $wrapped) { 91 | self.0 *= rhs; 92 | } 93 | } 94 | 95 | impl ops::Div<$wrapped> for $wrapper { 96 | type Output = Self; 97 | fn div(self, rhs: $wrapped) -> Self { 98 | Self(self.0 / rhs) 99 | } 100 | } 101 | 102 | impl ops::Div<$wrapper> for $wrapper { 103 | type Output = $wrapped; 104 | fn div(self, rhs: $wrapper) -> $wrapped { 105 | self.0 / rhs.0 106 | } 107 | } 108 | 109 | impl ops::DivAssign<$wrapped> for $wrapper { 110 | fn div_assign(&mut self, rhs: $wrapped) { 111 | self.0 /= rhs; 112 | } 113 | } 114 | }; 115 | } 116 | 117 | impl_arithmetic!(Bps, u32); 118 | 119 | /// A monotonic non-decreasing timer 120 | /// 121 | /// This uses the timer in the debug watch trace peripheral. This means, that if the 122 | /// core is stopped, the timer does not count up. This may be relevant if you are using 123 | /// cortex_m_semihosting::hprintln for debugging in which case the timer will be stopped 124 | /// while printing 125 | #[derive(Clone, Copy)] 126 | pub struct MonoTimer { 127 | frequency: Hertz, 128 | } 129 | 130 | impl MonoTimer { 131 | /// Creates a new `Monotonic` timer 132 | pub fn new(mut dwt: DWT, mut dcb: DCB, clocks: &Clocks) -> Self { 133 | dcb.enable_trace(); 134 | dwt.enable_cycle_counter(); 135 | 136 | // now the CYCCNT counter can't be stopped or reset 137 | 138 | MonoTimer { 139 | frequency: clocks.hclk(), 140 | } 141 | } 142 | 143 | /// Returns the frequency at which the monotonic timer is operating at 144 | pub fn frequency(self) -> Hertz { 145 | self.frequency 146 | } 147 | 148 | /// Returns an `Instant` corresponding to "now" 149 | pub fn now(self) -> Instant { 150 | Instant { 151 | now: DWT::cycle_count(), 152 | } 153 | } 154 | } 155 | 156 | /// A measurement of a monotonically non-decreasing clock 157 | #[derive(Clone, Copy)] 158 | pub struct Instant { 159 | now: u32, 160 | } 161 | 162 | impl Instant { 163 | /// Ticks elapsed since the `Instant` was created 164 | pub fn elapsed(self) -> u32 { 165 | DWT::cycle_count().wrapping_sub(self.now) 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/timer/delay.rs: -------------------------------------------------------------------------------- 1 | //! Delays 2 | 3 | use super::{FTimer, Instance, Timer}; 4 | use core::ops::{Deref, DerefMut}; 5 | use cortex_m::peripheral::SYST; 6 | use fugit::{MicrosDurationU32, TimerDurationU32}; 7 | 8 | /// Timer as a delay provider (SysTick by default) 9 | pub struct SysDelay(Timer); 10 | 11 | impl Deref for SysDelay { 12 | type Target = Timer; 13 | fn deref(&self) -> &Self::Target { 14 | &self.0 15 | } 16 | } 17 | 18 | impl DerefMut for SysDelay { 19 | fn deref_mut(&mut self) -> &mut Self::Target { 20 | &mut self.0 21 | } 22 | } 23 | 24 | impl SysDelay { 25 | /// Releases the timer resource 26 | pub fn release(self) -> Timer { 27 | self.0 28 | } 29 | } 30 | 31 | impl Timer { 32 | pub fn delay(self) -> SysDelay { 33 | SysDelay(self) 34 | } 35 | } 36 | 37 | impl SysDelay { 38 | pub fn delay(&mut self, us: MicrosDurationU32) { 39 | // The SysTick Reload Value register supports values between 1 and 0x00FFFFFF. 40 | const MAX_RVR: u32 = 0x00FF_FFFF; 41 | 42 | let mut total_rvr = us.ticks() * (self.clk.raw() / 1_000_000); 43 | 44 | while total_rvr != 0 { 45 | let current_rvr = total_rvr.min(MAX_RVR); 46 | 47 | self.tim.set_reload(current_rvr); 48 | self.tim.clear_current(); 49 | self.tim.enable_counter(); 50 | 51 | // Update the tracking variable while we are waiting... 52 | total_rvr -= current_rvr; 53 | 54 | while !self.tim.has_wrapped() {} 55 | 56 | self.tim.disable_counter(); 57 | } 58 | } 59 | } 60 | 61 | /// Periodic non-blocking timer that imlements [embedded_hal_02::blocking::delay] traits 62 | pub struct Delay(pub(super) FTimer); 63 | 64 | impl Deref for Delay { 65 | type Target = FTimer; 66 | fn deref(&self) -> &Self::Target { 67 | &self.0 68 | } 69 | } 70 | 71 | impl DerefMut for Delay { 72 | fn deref_mut(&mut self) -> &mut Self::Target { 73 | &mut self.0 74 | } 75 | } 76 | 77 | /// `Delay` with precision of 1 μs (1 MHz sampling) 78 | pub type DelayUs = Delay; 79 | 80 | /// `Delay` with precision of 1 ms (1 kHz sampling) 81 | /// 82 | /// NOTE: don't use this if your system frequency more than 65 MHz 83 | pub type DelayMs = Delay; 84 | 85 | impl Delay { 86 | /// Sleep for given time 87 | pub fn delay(&mut self, time: TimerDurationU32) { 88 | let mut ticks = time.ticks().max(1) - 1; 89 | while ticks != 0 { 90 | let reload = ticks.min(TIM::max_auto_reload()); 91 | 92 | // Write Auto-Reload Register (ARR) 93 | unsafe { 94 | self.tim.set_auto_reload_unchecked(reload); 95 | } 96 | 97 | // Trigger update event (UEV) in the event generation register (EGR) 98 | // in order to immediately apply the config 99 | self.tim.trigger_update(); 100 | 101 | // Configure the counter in one-pulse mode (counter stops counting at 102 | // the next updateevent, clearing the CEN bit) and enable the counter. 103 | self.tim.start_one_pulse(); 104 | 105 | // Update the tracking variable while we are waiting... 106 | ticks -= reload; 107 | // Wait for CEN bit to clear 108 | while self.tim.is_counter_enabled() { /* wait */ } 109 | } 110 | } 111 | 112 | pub fn max_delay(&self) -> TimerDurationU32 { 113 | TimerDurationU32::from_ticks(TIM::max_auto_reload()) 114 | } 115 | 116 | /// Releases the TIM peripheral 117 | pub fn release(mut self) -> FTimer { 118 | // stop counter 119 | self.tim.cr1_reset(); 120 | self.0 121 | } 122 | } 123 | 124 | impl fugit_timer::Delay for Delay { 125 | type Error = core::convert::Infallible; 126 | 127 | fn delay(&mut self, duration: TimerDurationU32) -> Result<(), Self::Error> { 128 | self.delay(duration); 129 | Ok(()) 130 | } 131 | } 132 | 133 | impl fugit_timer::Delay<1_000_000> for SysDelay { 134 | type Error = core::convert::Infallible; 135 | 136 | fn delay(&mut self, duration: MicrosDurationU32) -> Result<(), Self::Error> { 137 | self.delay(duration); 138 | Ok(()) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/timer/hal_1.rs: -------------------------------------------------------------------------------- 1 | //! Delay implementation based on general-purpose 32 bit timers and System timer (SysTick). 2 | //! 3 | //! TIM2 and TIM5 are a general purpose 32-bit auto-reload up/downcounter with 4 | //! a 16-bit prescaler. 5 | 6 | use core::convert::Infallible; 7 | use embedded_hal::delay::DelayNs; 8 | 9 | use super::{Delay, Instance, PwmChannel, SysDelay, WithPwm}; 10 | use fugit::ExtU32Ceil; 11 | 12 | impl DelayNs for SysDelay { 13 | fn delay_ns(&mut self, ns: u32) { 14 | self.delay(ns.nanos_at_least()); 15 | } 16 | 17 | fn delay_ms(&mut self, ms: u32) { 18 | self.delay(ms.millis_at_least()); 19 | } 20 | } 21 | 22 | impl DelayNs for Delay { 23 | fn delay_ns(&mut self, ns: u32) { 24 | self.delay(ns.micros_at_least()); 25 | } 26 | 27 | fn delay_us(&mut self, us: u32) { 28 | self.delay(us.micros_at_least()); 29 | } 30 | 31 | fn delay_ms(&mut self, ms: u32) { 32 | self.delay(ms.millis_at_least()); 33 | } 34 | } 35 | 36 | impl embedded_hal::pwm::ErrorType for PwmChannel { 37 | type Error = Infallible; 38 | } 39 | 40 | impl embedded_hal::pwm::SetDutyCycle for PwmChannel { 41 | fn max_duty_cycle(&self) -> u16 { 42 | self.get_max_duty() 43 | } 44 | fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { 45 | self.set_duty(duty); 46 | Ok(()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/timer/monotonic.rs: -------------------------------------------------------------------------------- 1 | //! RTIC Monotonic implementation 2 | 3 | use super::{FTimer, Instance}; 4 | use crate::rcc::Clocks; 5 | use core::ops::{Deref, DerefMut}; 6 | pub use fugit::{self, ExtU32}; 7 | use rtic_monotonic::Monotonic; 8 | 9 | pub struct MonoTimer { 10 | timer: FTimer, 11 | ovf: u32, 12 | } 13 | 14 | impl Deref for MonoTimer { 15 | type Target = FTimer; 16 | fn deref(&self) -> &Self::Target { 17 | &self.timer 18 | } 19 | } 20 | 21 | impl DerefMut for MonoTimer { 22 | fn deref_mut(&mut self) -> &mut Self::Target { 23 | &mut self.timer 24 | } 25 | } 26 | 27 | /// `MonoTimer` with precision of 1 μs (1 MHz sampling) 28 | pub type MonoTimerUs = MonoTimer; 29 | 30 | impl MonoTimer { 31 | /// Releases the TIM peripheral 32 | pub fn release(mut self) -> FTimer { 33 | // stop counter 34 | self.tim.cr1_reset(); 35 | self.timer 36 | } 37 | } 38 | 39 | pub trait MonoTimerExt: Sized { 40 | fn monotonic(self, clocks: &Clocks) -> MonoTimer; 41 | fn monotonic_us(self, clocks: &Clocks) -> MonoTimer { 42 | self.monotonic::<1_000_000>(clocks) 43 | } 44 | } 45 | 46 | macro_rules! mono { 47 | ($TIM:ty) => { 48 | impl MonoTimerExt for $TIM { 49 | fn monotonic(self, clocks: &Clocks) -> MonoTimer { 50 | FTimer::new(self, clocks).monotonic() 51 | } 52 | } 53 | 54 | impl FTimer<$TIM, FREQ> { 55 | pub fn monotonic(self) -> MonoTimer<$TIM, FREQ> { 56 | MonoTimer::<$TIM, FREQ>::_new(self) 57 | } 58 | } 59 | 60 | impl MonoTimer<$TIM, FREQ> { 61 | fn _new(timer: FTimer<$TIM, FREQ>) -> Self { 62 | // Set auto-reload value. 63 | timer.tim.arr().write(|w| w.arr().set(u16::MAX)); 64 | // Generate interrupt on overflow. 65 | timer.tim.egr().write(|w| w.ug().set_bit()); 66 | 67 | // Start timer. 68 | // Clear interrupt flag. 69 | timer.tim.sr().modify(|_, w| w.uif().clear_bit()); 70 | timer.tim.cr1().modify(|_, w| { 71 | // Enable counter. 72 | w.cen().set_bit(); 73 | // Overflow should trigger update event. 74 | w.udis().clear_bit(); 75 | // Only overflow triggers interrupt. 76 | w.urs().set_bit() 77 | }); 78 | 79 | Self { timer, ovf: 0 } 80 | } 81 | } 82 | 83 | impl Monotonic for MonoTimer<$TIM, FREQ> { 84 | type Instant = fugit::TimerInstantU32; 85 | type Duration = fugit::TimerDurationU32; 86 | 87 | unsafe fn reset(&mut self) { 88 | self.tim.dier().modify(|_, w| w.cc1ie().set_bit()); 89 | } 90 | 91 | #[inline(always)] 92 | fn now(&mut self) -> Self::Instant { 93 | let cnt = self.tim.cnt().read().cnt().bits() as u32; 94 | 95 | // If the overflow bit is set, we add this to the timer value. It means the `on_interrupt` 96 | // has not yet happened, and we need to compensate here. 97 | let ovf = if self.tim.sr().read().uif().bit_is_set() { 98 | 0x10000 99 | } else { 100 | 0 101 | }; 102 | 103 | Self::Instant::from_ticks(cnt.wrapping_add(ovf).wrapping_add(self.ovf)) 104 | } 105 | 106 | fn set_compare(&mut self, instant: Self::Instant) { 107 | let now = self.now(); 108 | let cnt = self.tim.cnt().read().cnt().bits(); 109 | 110 | // Since the timer may or may not overflow based on the requested compare val, we check 111 | // how many ticks are left. 112 | let val = match instant.checked_duration_since(now) { 113 | None => cnt.wrapping_add(0xffff), // In the past, RTIC will handle this 114 | Some(x) if x.ticks() <= 0xffff => instant.duration_since_epoch().ticks() as u16, // Will not overflow 115 | Some(_) => cnt.wrapping_add(0xffff), // Will overflow, run for as long as possible 116 | }; 117 | 118 | self.tim.ccr1().write(|w| w.ccr().set(val)); 119 | } 120 | 121 | fn clear_compare_flag(&mut self) { 122 | self.tim.sr().modify(|_, w| w.cc1if().clear_bit()); 123 | } 124 | 125 | fn on_interrupt(&mut self) { 126 | // If there was an overflow, increment the overflow counter. 127 | if self.tim.sr().read().uif().bit_is_set() { 128 | self.tim.sr().modify(|_, w| w.uif().clear_bit()); 129 | 130 | self.ovf += 0x10000; 131 | } 132 | } 133 | 134 | #[inline(always)] 135 | fn zero() -> Self::Instant { 136 | Self::Instant::from_ticks(0) 137 | } 138 | } 139 | }; 140 | } 141 | 142 | mono!(crate::pac::TIM2); 143 | mono!(crate::pac::TIM3); 144 | 145 | #[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))] 146 | mono!(crate::pac::TIM1); 147 | 148 | #[cfg(feature = "medium")] 149 | mono!(crate::pac::TIM4); 150 | 151 | #[cfg(any(feature = "high", feature = "connectivity"))] 152 | mono!(crate::pac::TIM5); 153 | 154 | #[cfg(all(feature = "stm32f103", feature = "high"))] 155 | mono!(crate::pac::TIM8); 156 | -------------------------------------------------------------------------------- /src/timer/pins.rs: -------------------------------------------------------------------------------- 1 | use crate::pac; 2 | 3 | pub trait CPin {} 4 | pub struct Ch; 5 | pub const C1: u8 = 0; 6 | pub const C2: u8 = 1; 7 | pub const C3: u8 = 2; 8 | pub const C4: u8 = 3; 9 | 10 | pub(crate) mod sealed { 11 | pub trait Remap { 12 | type Periph; 13 | const REMAP: u8; 14 | 15 | fn remap(mapr: &mut crate::afio::MAPR); 16 | } 17 | } 18 | 19 | macro_rules! remap { 20 | ($($name:ident: ($TIMX:ty, $state:literal, $P1:ident, $P2:ident, $P3:ident, $P4:ident, { $remapex:expr }),)+) => { 21 | $( 22 | pub struct $name; 23 | impl sealed::Remap for $name { 24 | type Periph = $TIMX; 25 | const REMAP: u8 = $state; 26 | 27 | fn remap(mapr: &mut crate::afio::MAPR) { 28 | mapr.modify_mapr($remapex); 29 | } 30 | } 31 | impl CPin<$name, 0> for crate::gpio::$P1 {} 32 | impl CPin<$name, 1> for crate::gpio::$P2 {} 33 | impl CPin<$name, 2> for crate::gpio::$P3 {} 34 | impl CPin<$name, 3> for crate::gpio::$P4 {} 35 | )+ 36 | } 37 | } 38 | 39 | #[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))] 40 | remap!( 41 | Tim1NoRemap: (pac::TIM1, 0b00, PA8, PA9, PA10, PA11, {|_, w| unsafe { w.tim1_remap().bits(Self::REMAP)}}), 42 | //Tim1PartialRemap: (pac::TIM1, 0b01, PA8, PA9, PA10, PA11), 43 | Tim1FullRemap: (pac::TIM1, 0b11, PE9, PE11, PE13, PE14, {|_, w| unsafe { w.tim1_remap().bits(Self::REMAP)}}), 44 | ); 45 | 46 | remap!( 47 | Tim2NoRemap: (pac::TIM2, 0b00, PA0, PA1, PA2, PA3, {|_, w| unsafe { w.tim2_remap().bits(Self::REMAP)}}), 48 | Tim2PartialRemap1: (pac::TIM2, 0b01, PA15, PB3, PA2, PA3, {|_, w| unsafe { w.tim2_remap().bits(Self::REMAP)}}), 49 | Tim2PartialRemap2: (pac::TIM2, 0b10, PA0, PA1, PB10, PB11, {|_, w| unsafe { w.tim2_remap().bits(Self::REMAP)}}), 50 | Tim2FullRemap: (pac::TIM2, 0b11, PA15, PB3, PB10, PB11, {|_, w| unsafe { w.tim2_remap().bits(Self::REMAP)}}), 51 | 52 | Tim3NoRemap: (pac::TIM3, 0b00, PA6, PA7, PB0, PB1, {|_, w| unsafe { w.tim3_remap().bits(Self::REMAP)}}), 53 | Tim3PartialRemap: (pac::TIM3, 0b10, PB4, PB5, PB0, PB1, {|_, w| unsafe { w.tim3_remap().bits(Self::REMAP)}}), 54 | Tim3FullRemap: (pac::TIM3, 0b11, PC6, PC7, PC8, PC9, {|_, w| unsafe { w.tim3_remap().bits(Self::REMAP)}}), 55 | ); 56 | 57 | #[cfg(feature = "medium")] 58 | remap!( 59 | Tim4NoRemap: (pac::TIM4, 0b00, PB6, PB7, PB8, PB9, {|_, w| w.tim4_remap().bit(Self::REMAP == 1)}), 60 | Tim4Remap: (pac::TIM4, 0b01, PD12, PD13, PD14, PD15, {|_, w| w.tim4_remap().bit(Self::REMAP == 1)}), 61 | ); 62 | -------------------------------------------------------------------------------- /src/usb.rs: -------------------------------------------------------------------------------- 1 | //! USB peripheral 2 | //! 3 | //! Requires the `stm32-usbd` feature. 4 | //! See 5 | //! for usage examples. 6 | 7 | use crate::pac::{RCC, USB}; 8 | use crate::rcc::{Enable, Reset}; 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 = false; 26 | const EP_MEMORY: *const () = 0x4000_6000 as _; 27 | const EP_MEMORY_SIZE: usize = 512; 28 | const EP_MEMORY_ACCESS_2X16: bool = false; 29 | 30 | fn enable() { 31 | unsafe { 32 | let rcc = &*RCC::ptr(); 33 | 34 | // Enable USB peripheral 35 | USB::enable(rcc); 36 | // Reset USB peripheral 37 | USB::reset(rcc); 38 | } 39 | } 40 | 41 | fn startup_delay() { 42 | // There is a chip specific startup delay. For STM32F103xx it's 1µs and this should wait for 43 | // at least that long. 44 | cortex_m::asm::delay(72); 45 | } 46 | } 47 | 48 | pub type UsbBusType = UsbBus; 49 | -------------------------------------------------------------------------------- /src/watchdog.rs: -------------------------------------------------------------------------------- 1 | //! Watchdog peripherals 2 | 3 | use crate::{ 4 | hal_02::watchdog::{Watchdog, WatchdogEnable}, 5 | pac::{DBGMCU as DBG, IWDG}, 6 | time::MilliSeconds, 7 | }; 8 | use fugit::ExtU32; 9 | 10 | /// Wraps the Independent Watchdog (IWDG) peripheral 11 | pub struct IndependentWatchdog { 12 | iwdg: IWDG, 13 | } 14 | 15 | const LSI_KHZ: u32 = 40; 16 | const MAX_PR: u8 = 8; 17 | const MAX_RL: u16 = 0xFFF; 18 | const KR_ACCESS: u16 = 0x5555; 19 | const KR_RELOAD: u16 = 0xAAAA; 20 | const KR_START: u16 = 0xCCCC; 21 | 22 | impl IndependentWatchdog { 23 | /// Wrap and start the watchdog 24 | pub fn new(iwdg: IWDG) -> Self { 25 | IndependentWatchdog { iwdg } 26 | } 27 | 28 | /// Debug independent watchdog stopped when core is halted 29 | pub fn stop_on_debug(&self, dbg: &DBG, stop: bool) { 30 | dbg.cr().modify(|_, w| w.dbg_iwdg_stop().bit(stop)); 31 | } 32 | 33 | fn setup(&self, timeout_ms: u32) { 34 | let mut pr = 0; 35 | while pr < MAX_PR && Self::timeout_period(pr, MAX_RL) < timeout_ms { 36 | pr += 1; 37 | } 38 | 39 | let max_period = Self::timeout_period(pr, MAX_RL); 40 | let max_rl = u32::from(MAX_RL); 41 | let rl = (timeout_ms * max_rl / max_period).min(max_rl) as u16; 42 | 43 | self.access_registers(|iwdg| { 44 | iwdg.pr().modify(|_, w| unsafe { w.pr().bits(pr) }); 45 | iwdg.rlr().modify(|_, w| w.rl().set(rl)); 46 | }); 47 | } 48 | 49 | fn is_pr_updating(&self) -> bool { 50 | self.iwdg.sr().read().pvu().bit() 51 | } 52 | 53 | /// Returns the interval in ms 54 | pub fn interval(&self) -> MilliSeconds { 55 | while self.is_pr_updating() {} 56 | 57 | let pr = self.iwdg.pr().read().pr().bits(); 58 | let rl = self.iwdg.rlr().read().rl().bits(); 59 | let ms = Self::timeout_period(pr, rl); 60 | ms.millis() 61 | } 62 | 63 | /// pr: Prescaler divider bits, rl: reload value 64 | /// 65 | /// Returns ms 66 | fn timeout_period(pr: u8, rl: u16) -> u32 { 67 | let divider: u32 = match pr { 68 | 0b000 => 4, 69 | 0b001 => 8, 70 | 0b010 => 16, 71 | 0b011 => 32, 72 | 0b100 => 64, 73 | 0b101 => 128, 74 | 0b110 => 256, 75 | 0b111 => 256, 76 | _ => panic!("Invalid IWDG prescaler divider"), 77 | }; 78 | (u32::from(rl) + 1) * divider / LSI_KHZ 79 | } 80 | 81 | fn access_registers A>(&self, mut f: F) -> A { 82 | // Unprotect write access to registers 83 | self.iwdg.kr().write(|w| unsafe { w.key().bits(KR_ACCESS) }); 84 | let a = f(&self.iwdg); 85 | 86 | // Protect again 87 | self.iwdg.kr().write(|w| unsafe { w.key().bits(KR_RELOAD) }); 88 | a 89 | } 90 | 91 | pub fn start(&mut self, period: MilliSeconds) { 92 | self.setup(period.ticks()); 93 | 94 | self.iwdg.kr().write(|w| unsafe { w.key().bits(KR_START) }); 95 | } 96 | 97 | pub fn feed(&mut self) { 98 | self.iwdg.kr().write(|w| unsafe { w.key().bits(KR_RELOAD) }); 99 | } 100 | } 101 | 102 | impl WatchdogEnable for IndependentWatchdog { 103 | type Time = MilliSeconds; 104 | 105 | fn start>(&mut self, period: T) { 106 | self.start(period.into()); 107 | } 108 | } 109 | 110 | impl Watchdog for IndependentWatchdog { 111 | fn feed(&mut self) { 112 | self.feed(); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /tools/check.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import json 4 | import subprocess 5 | import sys 6 | 7 | 8 | def run_inner(args): 9 | print("Running `{}`...".format(" ".join(args))) 10 | ret = subprocess.call(args) == 0 11 | print("") 12 | return ret 13 | 14 | 15 | def run(mcu, cargo_cmd): 16 | if mcu == "": 17 | return run_inner(cargo_cmd) 18 | else: 19 | return run_inner(cargo_cmd + ["--features={}".format(mcu)]) 20 | 21 | 22 | def main(): 23 | cargo_meta = json.loads( 24 | subprocess.check_output("cargo metadata --no-deps --format-version=1", 25 | shell=True, 26 | universal_newlines=True) 27 | ) 28 | 29 | crate_info = cargo_meta["packages"][0] 30 | 31 | features = ["{},rtic,high,stm32-usbd".format(x) 32 | for x in crate_info["features"].keys() 33 | if x.startswith("stm32f1")] 34 | 35 | if 'size_check' in sys.argv: 36 | cargo_cmd = ['cargo', 'build', '--release'] 37 | else: 38 | cargo_cmd = ['cargo', 'check'] 39 | 40 | if '--examples' in sys.argv: 41 | cargo_cmd += ['--examples'] 42 | 43 | if not all(map(lambda f: run(f, cargo_cmd), 44 | features)): 45 | sys.exit(-1) 46 | 47 | 48 | if __name__ == "__main__": 49 | main() 50 | 51 | --------------------------------------------------------------------------------